home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cocktail
/
docme.lha
/
doc.me
/
minilax.me
< prev
next >
Wrap
Text File
|
1992-09-25
|
100KB
|
3,254 lines
.\" use: pic | tbl | eqn | ditroff -me
.\"
.\" "@(#)bibmac.me 2.2 9/9/83";
.de IP
.ip \\$1 \\$2
..
.de LP
.lp
..
.\" @(#)bmac.std 2.2 9/9/83;
.\" standard format troff commands
.\" citation formatting strings
.ds [[ [
.ds ]] ]
.ds ], ,\|
.ds ]- -
.ds [. " \&
.ds .] .
.ds [, " \&
.ds ,] ,
.ds [? " \&
.ds ?] ?
.ds [: " \&
.ds :] :
.ds [; " \&
.ds ;] ;
.ds [! " \&
.ds !] !
.ds [" " \&
.ds "] \&"
.ds [' " \&
.ds '] '
.ds [< " \&
.ds >]
.\" reference formmating strings
.ds a] " \&
.ds b] , \&
.ds c] , \&
.ds n] "\& and \&
.ds m] "\& and \&
.ds p] .
.\" reference formmating macros
.de s[ \" start reference
.nh
.IP [\\*([F] 5m
..
.de e[ \" end reference
.[-
..
.de [] \" start to display collected references
.LP
..
.de ][ \" choose format
.ie !"\\*([J"" \{\
. ie !"\\*([V"" .nr t[ 1 \" journal
. el .nr t[ 5 \" conference paper
.\}
.el .ie !"\\*([B"" .nr t[ 3 \" article in book
.el .ie !"\\*([R"" .nr t[ 4 \" technical report
.el .ie !"\\*([I"" .nr t[ 2 \" book
.el .nr t[ 0 \" other
.\\n(t[[
..
.de 0[ \" other
.s[
.if !"\\*([A"" \\*([A\\c
.if !"\\*([T"" , \\*([T\\c
.if !"\\*([V"" , Vol. \\*([V\\c
.if !"\\*([O"" , \\*([O\\c
.if !"\\*([D"" , \\*([D\\c
\&.
.e[
..
.de 1[ \" journal article
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
\\fI\\*([J \\*([V\\fP\c
.if !"\\*([N"" ,\\*([N
.if !"\\*([D"" (\\*([D)\c
.if !"\\*([P"" , \\*([P\c
.if !"\\*([I"" , \\*([I\c
\\&.
.if !"\\*([O"" \\*([O.
.e[
..
.de 2[ \" book
.s[
.ie !"\\*([A"" \\*([A,
.el .if !"\\*([E"" \{\
. ie \\n([E-1 \\*([E, eds.,
. el \\*([E, ed.,\}
.if !"\\*([T"" \\fI\\*([T\\fP,
.rm a[
.if !"\\*([I"" .ds a[ \\*([I
.if !"\\*([C"" \{\
. if !"\\*(a["" .as a[ , \\&
. as a[ \\*([C\}
.if !"\\*([D"" \{\
. if !"\\*(a["" .as a[ , \\&
. as a[ \\*([D\}
\\*(a[.
.if !"\\*([G"" Gov. ordering no. \\*([G.
.if !"\\*([O"" \\*([O.
.e[
..
.de 3[ \" article in book
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
in \\fI\\*([B\\fP\c
.if !"\\*([V"" , vol. \\*([V
.if !~\\*([E~~ \{\
. ie , \\n([E-1 \\*([E (editors)\c
. el , \\*([E (editor)\c\}
.if !"\\*([I"" , \\*([I\c
.if !"\\*([C"" , \\*([C\c
.if !"\\*([D"" , \\*([D\c
.if !"\\*([P"" , \\*([P\c
\\&.
.if !"\\*([O"" \\*([O.
.e[
..
.de 4[ \" report
.s[
.if !"\\*([A"" \\*([A,
.if !~\\*([E~~ \{\
. ie \\n([E-1 \\*([E, editors.
. el \\*([E, editor.\}
\\*([T,
\\*([R\c
.if !"\\*([G"" \& (\\*([G)\c
.if !"\\*([I"" , \\*([I\c
.if !"\\*([C"" , \\*([C\c
.if !"\\*([D"" , \\*([D\c
\\&.
.if !"\\*([O"" \\*([O.
.e[
..
.de 5[ \" conference paper
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
\\fI\\*([J\\fP,
.if !"\\*([C"" \\*([C,
.if !"\\*([D"" \\*([D\c
.if !"\\*([P"" , \\*([P\c
\\&.
.if !"\\*([O"" \\*([O.
.e[
..
.de [- \" clean up after yourself
.rm [A [B [C [D
.rm [E [F [G
.rm [I [J [K
.rm [N [O [P
.rm [R [T
.rm [V [W
..
.\" @(#)bmac.std 2.2 8/24/83;
.\" standard format troff commands
.\" citation formatting strings
.ds [[ [
.ds ]] ]
.ds ], ,\|
.ds ]- -
.ds [. " \&
.ds .] .
.ds [, " \&
.ds ,] ,
.ds [< " \&
.ds >]
.\" reference formmating strings
.ds c] , \&
.ds n] "" and \&
.ds m] "" and \&
.ds a] " \&
.\" reference formmating macros
.de s[ \" start reference
.nh
.IP [\\*([F] 5m
..
.de e[ \" end reference
.[-
..
.de [] \" start to display collected references
.SH
References
.LP
..
.de ][ \" choose format
.ie !"\\*([J"" \{\
. ie !"\\*([V"" .nr t[ 1 \" journal
. el .nr t[ 5 \" conference paper
.\}
.el .ie !"\\*([B"" .nr t[ 3 \" article in book
.el .ie !"\\*([R"" .nr t[ 4 \" technical report
.el .ie !"\\*([I"" .nr t[ 2 \" book
.el .nr t[ 0 \" other
.\\n(t[[
..
.de 0[ \" other
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
.if !"\\*([O"" \\*([O\c
.if !"\\*([D"" , \\*([D\c
\&.
.e[
..
.de 1[ \" journal article
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
\\fI\\*([J \\*([V\\fP,
.if !"\\*([N"" \\*([N
.if !"\\*([D"" (\\*([D),
.if !"\\*([P"" \\*([P\c
.if !"\\*([I"" , \\*([I\c
\\&.
.if !"\\*([O"" \\*([O.
.e[
..
.de 2[ \" book
.s[
.ie !"\\*([A"" \\*([A,
.el .if !"\\*([E"" \{\
. ie \\n([E-1 \\*([E, eds.,
. el \\*([E, ed.,\}
.if !"\\*([T"" \\fI\\*([T\\fP,
.rm a[
.if !"\\*([I"" .ds a[ \\*([I
.if !"\\*([C"" \{\
. if !"\\*(a["" .as a[ , \\&
. as a[ \\*([C\}
.if !"\\*([D"" \{\
. if !"\\*(a["" .as a[ , \\&
. as a[ \\*([D\}
\\*(a[.
.if !"\\*([G"" Gov. ordering no. \\*([G.
.if !"\\*([O"" \\*([O.
.e[
..
.de 3[ \" article in book
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
in \\fI\\*([B\\fP,
.if !"\\*([V"" vol. \\*([V,
.if !"\\*([E"" \\*([E (ed.),
.if !"\\*([I"" \\*([I,
.if !"\\*([C"" \\*([C,
.if !"\\*([D"" \\*([D\c
.if !"\\*([P"" , \\*([P\c
\\&.
.if !"\\*([O"" \\*([O.
.e[
..
.de 4[ \" report
.s[
.if !"\\*([A"" \\*([A,
\\*([T,
\\*([R\c
.if !"\\*([G"" \& (\\*([G)\c
.if !"\\*([I"" , \\*([I\c
.if !"\\*([C"" , \\*([C\c
.if !"\\*([D"" , \\*([D\c
\\&.
.if !"\\*([O"" , \\*([O.
.e[
..
.de 5[ \" conference paper
.s[
.if !"\\*([A"" \\*([A,
.if !"\\*([T"" \\*([T,
\\fI\\*([J\\fP,
.if !"\\*([C"" \\*([C\c
.if !"\\*([D"" , \\*([D\c
.if !"\\*([P"" , \\*([P\c
\\&.
.if !"\\*([O"" , \\*([O.
.e[
..
.de [- \" clean up after yourself
.rm [A [B [C [D
.rm [E [F [G
.rm [I [J [K
.rm [N [O [P
.rm [R [T
.rm [V [W
..
. so head
.hc ~
.ds ], ,
.EQ
delim off
.EN
.b " "
.sp 1c
.ta 9c
.ft R
.sz 12
\l'17.1c'
.nf
Specification of a
MiniLAX-Interpreter
J. Grosch
\l'17.1c'
.sp 12.5c
\l'17.1c'
.ft H
.nf
GESELLSCHAFT F\*UR MATHEMATIK
UND DATENVERARBEITUNG MBH
FORSCHUNGSSTELLE F\*UR
PROGRAMMSTRUKTUREN
AN DER UNIVERSIT\*AT KARLSRUHE
.r
\l'17.1c'
.bp
.oh '''%'
.eh '''%'
.ce 99
.sz 20
.b " "
.sp 2
Project
.sp
.b "Compiler Generation"
.sp
.sz 12
\l'15c'
.sp
.sz 16
.b "Specification of a MiniLAX-Interpreter"
.sp 2
Josef Grosch
.sp 2
.sz 14
Nov. 22, 1991
.sp
.sz 12
\l'15c'
.sp 2
Report No. 22
.sp 2
Copyright \(co 1991 GMD
.sp 2
Gesellschaft f\*ur Mathematik und Datenverarbeitung mbH
Forschungsstelle an der Universit\*at Karlsruhe
Vincenz-Prie\*snitz-Str. 1
D-7500 Karlsruhe
.ce 0
.fi
.bp 1
.ce 99
.b "Specification of a MiniLAX-Interpreter"
.sp
J. Grosch
GMD Forschungsstelle an der Universit\*at Karlsruhe
Vincenz-Prie\*snitz-Str. 1, D-7500 Karlsruhe, Germany
Tel: +721-6622-26
E-Mail: grosch@karlsruhe.gmd.de
.ce 0
.sp
.sh 1 Introduction
.pp
This paper describes the specification of a MiniLAX interpreter using the
following tools from the GMD Toolbox for Compiler Construction
\*([[GrE90\*(]]: The scanner generator
.i rex
\*([[Gro87b\*(]],
the parser generator
.i lalr
\*([[GrV88\*(]],
the generator for abstract syntax trees
.i ast
\*([[Gro91a\*(]],
the attribute evaluator generator
.i ag
\*([[Gro89\*(]],
and the generator for the transformation of attributed trees
.i puma
\*([[Gro91b\*(]].
The target language is Modula-2. The compiler parts which are not
generated by the tools are either taken from a library of reusable
modules\*([<\*([[Gro87a\*(]]\*(>] or are provided as hand-written Modula-2 code.
.pp
The rest of this report is organized as follows:
Section 2 defines the source language MiniLAX.
Section 3 defines the intermediate language ICode which is the input of the interpreter.
Section 4 explains the structure of the generated compiler.
Section 5 contains the specifications for the compiler.
.sh 1 MiniLAX
.pp
The programming language MiniLAX (Mini LAnguage eXample) is a Pascal
relative.
To be more specific it is a subset of the example language LAX\*([,\*([[WaG84\*(]]\*(,]
which is used to illustrate problems in compiler construction.
MiniLAX contains a carefully selected set of language concepts:
.ip \(bu
types
.ip \(bu
type coercion
.ip \(bu
overloaded operators
.ip \(bu
arrays
.ip \(bu
procedures
.ip \(bu
reference and value parameters
.ip \(bu
nested scopes
.pp
Concepts with a low didactical value and concepts
that would make the language unnecessary complex have been
left out, along with
.q "syntactic sugar" .
.sh 2 "Summary of the Language"
.pp
A computer program consists of two essential parts, a
description of
.i "actions"
which are to be performed, and a description of the
.i "data" ,
which are manipulated by these actions.
Actions are described by
.i "statements" ,
and data are described by
.i "declarations" .
.pp
The data are represented by constants and values of
.i "variables" .
Every variable occurring in a statement must be introduced
by a
.i "variable declaration"
which associates an identifier and a data type with that variable.
The
.i "data type"
essentially defines the set of values which may be assumed by that variable.
The data type is directly described in the variable
declaration.
.pp
There exist three
.i "basic types" :
\fCBoolean\fP, \fCinteger\fP, and \fCreal\fP.
The values of the type Boolean are denoted by reserved
identifiers, the numeric values are denoted by numbers.
.pp
.i "Array types"
are defined by describing the types of their components and an integer range.
A component of an array value is selected by an integer
.i "index" .
The type of the component is the component type of the
corresponding array type.
.pp
The most fundamental statement is the
.i "assignment statement" .
It specifies that a newly computed value be assigned to a
variable (or a component of a variable).
The value is obtained by evaluating an
.i "expression" .
Expressions consist of variables, constants and operators
operating on the denoted quantities and producing new
values.
MiniLAX defines a fixed set of operators, each of which can be
regarded as describing a mapping from the operand types into
the result type.
The set of operators is subdivided into
.(b
Arithmetic operators: addition and multiplication
Boolean operators: negation
Relational operators: comparison
.)b
The result of a comparison is of type \fCBoolean\fP.
The
.i "procedure statement"
causes the execution of the designated procedure (see below).
Assignment and procedure statements are the components or building blocks of
.i "structured statements" ,
which specify sequential, selective, or repeated execution of their components.
Sequential execution of statements is specified by
.i "statement sequences" ,
selective execution by the
.i "if statement" ,
and repeated execution by the
.i "while statement" .
The if statement serves to make the execution of two
alternative statements dependent on the value of a Boolean
expression.
The while statement serves to execute a statement while a
Boolean expression is true.
.pp
A statement sequence can be given a name (identifier), and be
referenced through that identifier.
The statement sequence is then called a
.i "procedure" ,
and its declaration a
.i "procedure declaration" .
Such a declaration may additionally contain a set of
variable declarations and further procedure declarations.
The variables and procedures thus declared can be referenced
only within the procedure itself, and are therefore called
.i "local"
to the procedure.
Their identifiers have significance only within the program
text which constitutes the procedure declaration and which
is called the
.i "scope"
of these identifiers.
Since procedures may be declared local to other procedures,
scopes may be nested.
Entities which are declared in the main program, i.e. not
local to some procedure, are called
.i "global" .
A procedure has a fixed number of parameters, each of which
is denoted within the procedure by an identifier called the
.i "formal parameter" .
Upon an activation of the procedure statement, an actual
quantity has to be indicated for each parameter which can be
referenced from within the procedure through the formal
parameter.
This quantity is called the
.i "actual parameter" .
There are two kinds of parameters: value parameters and
variable parameters.
In the first case, the actual parameter is an expression
which is evaluated once.
The formal parameter represents a local variable to which
the result of this evaluation is assigned before the
execution of the procedure.
In the case of a variable parameter, the actual parameter is
a variable and the formal parameter stands for this
variable.
Possible indices are evaluated before execution of the
procedure.
.sh 2 "Notation, Terminology, and Vocabulary"
.pp
The syntax is described in extended Backus-Naur form.
Syntactic constructs are denoted by (abbreviated) English
words consisting of upper and lower case letters, and
containing at least one lower-case letter.
The angular brackets < and > are omitted.
Strings of letters consisting solely of upper-case letters
stand for themselves, i.e. for reserved identifiers of the
language.
Strings of characters enclosed in single quotes '\ ' are also
to be taken literally.
Square brackets [\ ] denote optional constructs.
Curly brackets {\ } stand for zero or more repetitions of
the enclosed construct.
Alternative constructs are separated by a vertical bar |.
Parentheses (\ ) are used for grouping.
.pp
The basic vocabulary of MiniLAX consists of basic symbols
classified into delimiters, identifiers and constants.
.pp
Spaces, line ends, and comments may occur anywhere in a
program except within a basic symbol.
At least one space, line end or comment must occur between
any two adjacent identifiers or constants.
Otherwise, spaces, line ends, and comments do not influence
the meaning of a program.
.pp
A
.i "comment"
has the form
.TS
center;
l.
\fC'(*'\fP any sequence of characters not containing \(lq*)\(rq \fC'*)'\fP
.TE
.sh 3 "Delimiters"
Delimiters are reserved identifiers or (strings of) special
characters.
.TS
center;
l l.
\fIDelim\fP ::= \fC':'\fP | \fC';'\fP | \fC':='\fP | \fC'('\fP | \fC')'\fP | \fC'.'\fP | \fC','\fP
|\fC'..'\fP | \fC'['\fP | \fC']'\fP | \fC'+'\fP | \fC'*'\fP | \fC'<'\fP
|\fC'ARRAY'\fP | \fC'BEGIN'\fP | \fC'BOOLEAN'\fP | \fC'DECLARE'\fP | \fC'DO'\fP
|\fC'ELSE'\fP | \fC'END'\fP | \fC'FALSE'\fP | \fC'IF'\fP | \fC'INTEGER'\fP | \fC'NOT'\fP
|\fC'OF'\fP | \fC'PROCEDURE'\fP | \fC'PROGRAM'\fP | \fC'READ'\fP | \fC'REAL'\fP
|\fC'THEN'\fP | \fC'TRUE'\fP | \fC'VAR'\fP | \fC'WHILE'\fP | \fC'WRITE'\fP
.TE
.sh 3 "Identifiers"
Identifiers serve to denote variables and procedures.
Their association must be unique within their scope of
validity, i.e. within the procedure or function in which
they are declared.
.TS
center;
l l.
\fIId\fP ::= \fILetter\fP { \fILetter\fP | \fIDigit\fP }
.TE
All letters and digits of an identifier are significant.
Upper and lower case letters are distinguished.
Delimiters are reserved identifiers that can not be used
otherwise.
.sh 3 "Numbers"
The usual decimal notation is used for numbers, which are
the constants of the data types \fCinteger\fP and \fCreal\fP.
The letter 'E' preceding the scale factor is pronounced
as
.q 'times 10 to the power of' .
.TS
center;
l l.
\fIIntConst\fP ::= \fIDigit\fP { \fIDigit\fP }
\fIRealConst\fP ::= [ \fIIntConst\fP ] \fC'.'\fP \fIIntConst\fP [ \fIScaleFactor\fP ]
\fIScaleFactor\fP ::= \fC'E'\fP [ \fC'+'\fP | \fC'-'\fP ] \fIIntConst\fP
.TE
Examples:
.sz -2
.(b
.TS
l l l l.
\fC1 100 .1 87.35E-8\fP
.TE
.)b
.sz +2
.sh 2 "Data Types"
.pp
A data type determines the set of values which variables of
that type may assume.
.TS
center;
l l.
\fIType\fP ::= \fISimpleType\fP | \fIArrayType\fP
.TE
.sh 3 "Simple Types"
.TS
center;
l l.
\fISimpleType\fP ::= \fC'INTEGER'\fP | \fC'REAL'\fP | \fC'BOOLEAN'\fP
.TE
The values of type INTEGER are a subset of the whole numbers
defined by individual implementations.
Its values are the integers.
.pp
The values of type REAL are a subset of the real numbers
depending on a particular implementation.
The values are denoted by real numbers.
.pp
The values of type BOOLEAN are the truth values denoted by
the reserved identifiers TRUE and FALSE.
.sh 3 "Array Types"
An array type is a structure consisting of a fixed number of
components which are all of the same type, called the
.i "component type" .
The elements of the array are designated by integer
.i "indices" .
The array type specifies the component type as well as a
subrange of the integers to be used as indices.
.TS
center;
l l.
\fIArrayType\fP ::= \fC'ARRAY'\fP \fC'['\fP \fIIntConst\fP \fC'..'\fP \fIIntConst\fP \fC']'\fP \fC'OF'\fP \fIType\fP
.TE
Examples:
.sz -2
.(b
\fCARRAY [1..100] OF INTEGER\fP
\fCARRAY [4..7] OF ARRAY [2..2] OF BOOLEAN\fP
.)b
.sz +2
The index range must contain at least one element, i.e. the
lower bound of an index range must not exceed the upper
bound.
.sh 2 "Declarations and Denotations of Variables"
.pp
Variable declarations consist of an identifier denoting the
new variable, followed by its type.
.TS
center;
l l.
\fIVarDecl\fP ::= \fIId\fP \fC':'\fP \fIType\fP
.TE
Examples:
.sz -2
.(b
\fCi: INTEGER\fP
\fCr: REAL\fP
\fCb: BOOLEAN\fP
\fCa: ARRAY [4..7] OF ARRAY [2..2] OF INTEGER\fP
.)b
.sz +2
Denotations of variables either designate an entire variable
or a component of an array variable.
Variables occurring in examples in subsequent chapters are
assumed to be declared as indicated above.
.TS
center;
l l.
\fIVar\fP ::= \fIId\fP | \fIVar\fP \fC'['\fP \fIExpr\fP \fC']'\fP
.TE
Examples:
.sz -2
.(b
.TS
l l.
\fCi a[4][2]\fP
.TE
.)b
.sz +2
An entire variable is denoted by its identifier.
A component of an array variable is denoted by the variable
followed by an index expression.
The value of the index expression must lie in the range of
the indices of the corresponding array type.
.sh 2 "Expressions"
.pp
Expressions are constructs denoting rules of computation for
obtaining values of variables and generating new values by
the application of operators.
Expressions consist of operators and operands, i.e.
variables and constants.
.pp
The rules of composition specify operator
.i "precedences"
according to four classes of operators.
The operator NOT has the highest precedence, followed by the
multiplying operator '*', the adding operator '+', and
finally, with the lowest precedence, the relational operator
'<'.
Sequences of operators of the same precedence are executed
from left to right.
.TS
center;
l l.
\fIExpr\fP ::= \fIExpr\fP ( \fC'+'\fP | \fC'*'\fP | \fC'<'\fP ) \fIExpr\fP | \fC'NOT'\fP \fIExpr\fP
|\fC'('\fP \fIExpr\fP \fC')'\fP | \fIVar\fP | \fIIntConst\fP | \fIRealConst\fP
| \fC'TRUE'\fP | \fC'FALSE'\fP
.TE
Examples:
.sz -2
.ll -2c
.(b
.TS
expand;
l l l l l l.
\fCi 15 TRUE 2*(i+r) NOT b NOT (i<1)\fP
.TE
.)b
.ll +2c
.sz +2
The operators are summarized in the following table:
.TS
box center;
c s s s s s
c|c|l|l|l|l.
Table of Operators
.sp
Priority Operator left right Result Operation
\^ \^ Operand Operand \^ \^
_
4 \fCNOT\fP BOOLEAN BOOLEAN negation
_
3 \fC*\fP INTEGER INTEGER INTEGER integer multiplication
REAL REAL REAL real multiplication
_
2 \fC+\fP INTEGER INTEGER INTEGER integer addition
REAL REAL REAL real addition
_
1 \fC<\fP INTEGER INTEGER BOOLEAN integer comparison
REAL REAL BOOLEAN real comparison
BOOLEAN BOOLEAN BOOLEAN boolean comparison
.TE
Note that, for Boolean values, FALSE < TRUE.
.sh 2 "Statements"
.pp
Statements denote algorithmic actions, and are said to be
.i "executable" .
.TS
center;
l l.
\fIStat\fP ::= \fIAssignStat\fP | \fICondStat\fP | \fILoopStat\fP | \fIProcStat\fP
.TE
.sh 3 "Statement sequences"
A statement sequence specifies that its component
statements are to be executed in the same sequence as they
are written.
.TS
center;
l l.
\fIStatSeq\fP ::= \fIStat\fP { \fC';'\fP \fIStat\fP }
.TE
.sh 3 "Assignment Statements"
The assignment statement serves to replace the current value
of a variable by a new value specified as an expression.
.TS
center;
l l.
\fIAssignStat\fP ::= \fIVar\fP \fC':='\fP \fIExpr\fP
.TE
Examples:
.sz -2
.(b
\fCi := i+1\fP
\fCr := r*3.141592\fP
\fCb := i<1\fP
\fCa[4][2] := r\fP
.)b
.sz +2
The variable and the expression must be of identical type,
with the following exception being permitted: The type of
the variable is REAL, and the type of the expression is
INTEGER.
In any case, the variable must be of a simple type.
.sh 3 "Procedure Statements"
A procedure statement serves to execute the procedure
denoted by the procedure identifier.
The procedure statement may contain a list of
.i "actual parameters"
which are substituted in place of their corresponding
.i "formal parameters"
defined in the procedure declaration.
The correspondence is established by the positions of the
parameters in the lists of actual and formal parameters
respectively.
There exist two kinds of parameters: value
parameters and variable parameters.
.pp
In the case of a
.i "value parameter" ,
the actual parameter must be an expression (of which a variable is a simple
case).
The corresponding formal parameter represents a local
variable of the called procedure, and the current value of
the expression is initially assigned to this variable.
Value parameters must have a simple type.
In the case of a
.i "variable parameter" ,
the actual parameter must be a variable of the same type,
and the corresponding formal parameter
represents this actual variable during the entire execution
of the procedure.
If this variable is a component of an array, its index is
evaluated when the procedure is called.
A variable parameter must be used whenever the parameter
represents a result of the procedure.
.TS
center;
l l.
\fIProcStat\fP ::= \fIId\fP [ \fC'('\fP \fIExpr\fP { \fC','\fP \fIExpr\fP } \fC')'\fP ]
.TE
Examples:
.sz -2
.(b
.TS
l l.
\fCnext Transpose(a,m,n)\fP
.TE
.)b
.sz +2
.sh 3 "Conditional Statements"
The if statement specifies that a statement be executed only
if a certain condition (Boolean expression) is true.
If it is false, the statement following the delimiter ELSE
is to be executed.
.TS
center;
l l.
\fICondStat\fP ::= \fC'IF'\fP \fIExpr\fP \fC'THEN'\fP \fIStatSeq\fP \fC'ELSE'\fP \fIStatSeq\fP \fC'END'\fP
.TE
Examples:
.sz -2
.(b
\fCIF i < 0 THEN i := 1 ELSE i := 2 END\fP
.)b
.sz +2
The expression between the delimiters \fCIF\fP and \fCTHEN\fP must be of
type Boolean.
.sh 3 "Repetitive Statements"
The while statement specifies that a certain statement is to
be executed repeatedly.
.TS
center;
l l.
\fILoopStat\fP ::= \fC'WHILE'\fP \fIExpr\fP \fC'DO'\fP \fIStatSeq\fP \fC'END'\fP
.TE
The expression controlling repetition must be of type
Boolean.
The statement is repeatedly executed as long as the
expression is true.
If it evaluates to false at the beginning, the statement is
not executed at all.
The while statement
.sz -2
.(b
\fCWHILE b DO s END\fP
.)b
.sz +2
is equivalent to
.sz -2
.(b
\fCIF b\fP
\fCTHEN s; WHILE b DO s END\fP
\fCELSE (* nothing *)\fP
\fCEND\fP
.)b
.sz +2
Examples:
.sz -2
.(b
\fCWHILE a [i] < r DO i := i + 1 END\fP
\fCWHILE i < n DO\fP
\fC r := 2 * r;\fP
\fC i := i + 1\fP
\fCEND\fP
.)b
.sz +2
.sh 3 "Procedure Declarations"
Procedure declarations serve to define parts of programs and
to associate identifiers with them so that they can be
activated by procedure statements.
.TS
center;
l l.
\fIProcDecl\fP ::= \fIProcHead\fP \fC';'\fP \fIBlock\fP
\fIBlock\fP ::= \fC'DECLARE'\fP \fIDecl\fP { \fC';'\fP \fIDecl\fP } \fC'BEGIN'\fP \fIStatSeq\fP \fC'END'\fP
\fIDecl\fP ::= \fIVarDecl\fP | \fIProcDecl\fP
.TE
The
.i "procedure heading"
specifies the identifier naming the procedure and
the formal parameter identifiers (if any).
The parameters are either value or variable parameters.
.TS
center;
l l.
\fIProcHead\fP ::= \fC'PROCEDURE'\fP \fIId\fP [ \fC'('\fP \fIFormal\fP { \fC';'\fP \fIFormal\fP } \fC')'\fP ]
\fIFormal\fP ::= [ \fC'VAR'\fP ] \fIId\fP \fC':'\fP \fIType\fP
.TE
If a formal starts with the delimiter VAR it specifies a
variable parameter, otherwise a value parameter.
.pp
The statement sequence of the block specifies the algorithmic
actions to be executed upon an activation of the procedure
by a procedure statement.
.pp
All identifiers introduced in the formal parameter part of
the procedure heading and in the declaration part of the
associated block are
.i "local"
to the procedure declaration which is called the
.i "scope"
of these identifiers.
They are not known outside their scope.
In the case of local variables, their values are undefined
at the beginning of the statement part.
.pp
The use of the procedure identifier in a procedure statement
within its declaration implies recursive execution of the
procedure.
.pp
Examples of procedure declarations:
.sz -2
.(b
\fCPROCEDURE ReadPosInteger (VAR i: INTEGER);\fP
\fCDECLARE\fP
\fC j: INTEGER;\fP
\fCBEGIN\fP
\fC i := 0;\fP
\fC WHILE NOT (0 < i) DO READ (i) END\fP
\fCEND\fP
.)b
.(b
\fCPROCEDURE Sort (VAR a: ARRAY [1..10] OF REAL; n: INTEGER);\fP
\fCDECLARE\fP
\fC i: INTEGER; j: INTEGER; k: INTEGER; h: REAL;\fP
\fCBEGIN\fP
\fC i := 1;\fP
\fC WHILE i < n DO (* a [1], ... , a [i] is sorted *)\fP
\fC j := i; k := i;\fP
\fC WHILE j < n DO (* a [k] = min {a [i], ... , a [j]} *)\fP
\fC j := j + 1;\fP
\fC IF a [j] < a [k] THEN k := j ELSE k := k END\fP
\fC END;\fP
\fC h := a [i]; a [i] := a [k]; a [k] := h;\fP
\fC i := i + 1\fP
\fC END\fP
\fCEND\fP
.)b
.sz +2
.sh 2 "Input and Output"
.pp
Input and output of values of simple types
is achieved by the standard procedures READ and
WRITE.
.pp
The procedure READ takes one actual parameter which must be
a variable of a simple type.
It reads a value of the corresponding type from the standard
input and assigns it to that variable.
.pp
The procedure WRITE takes one actual parameter which must be
an expression with a simple type.
It writes the value of that expression onto the standard
output.
.pp
Example:
.sz -2
.(b
\fC(* read integers and write until a nonpositive number is read *)\fP
\fCREAD (i);\fP
\fCWHILE 0 < i DO\fP
\fC WRITE (i); READ (i)\fP
\fCEND\fP
.)b
.sz +2
.sh 2 "Programs"
.pp
A MiniLAX program has the form of a procedure declaration except
for its heading.
.TS
center;
l l.
\fIProgram\fP ::= \fC'PROGRAM'\fP \fIId\fP \fC';'\fP \fIBlock\fP \fC'.'\fP
.TE
The identifier following the symbol PROGRAM is the program
name; it has no further significance inside the program.
.sp
Example:
.sz -2
.(b
\fCPROGRAM test;\fP
\fC (* read, sort and write an array of n numbers *)\fP
\fC (* this program shows the following features: *)\fP
\fC (* procedure calls from main level, to a local, and to *)\fP
\fC (* a global procedure *)\fP
\fC (* access to a global array *)\fP
\fC (* access to local, global and intermediate variables *)\fP
\fC (* recursion *)\fP
\fC (* reading and writing of all types *)\fP
\fC (* integer to real conversion *)\fP
.)b
.(b
\fCDECLARE\fP
\fC test : BOOLEAN;\fP
\fC n : INTEGER;\fP
\fC a : ARRAY [1..100] OF REAL;\fP
.)b
.(b
\fC PROCEDURE skip; (* do nothing *)\fP
\fC DECLARE\fP
\fC n: INTEGER\fP
\fC BEGIN\fP
\fC n := n\fP
\fC END;\fP
.)b
.(b
\fC PROCEDURE read (VAR n: INTEGER; VAR a: ARRAY [1..100] OF REAL);\fP
\fC DECLARE\fP
\fC i: INTEGER\fP
\fC BEGIN\fP
\fC WRITE (TRUE); READ (test);\fP
\fC WRITE (5); READ (n);\fP
\fC i := 1;\fP
\fC WHILE i < n DO\fP
\fC i := i + 1; WRITE (1.0E-7); READ (a [i])\fP
\fC END\fP
\fC END;\fP
.)b
.(b
\fC PROCEDURE write (m: INTEGER); (* write a [m..n] *)\fP
\fC DECLARE\fP
\fC x: INTEGER\fP
\fC BEGIN\fP
\fC WRITE (a [m]);\fP
\fC IF m < n THEN write (m + 1) ELSE skip END\fP
\fC END;\fP
.)b
.(b
\fC PROCEDURE sort (VAR a: ARRAY [1..100] OF REAL); (* sort a [1..n] *)\fP
\fC DECLARE\fP
\fC i : INTEGER;\fP
\fC j : INTEGER;\fP
\fC k : INTEGER;\fP
\fC h : REAL;\fP
\fC ok: BOOLEAN;\fP
.)b
.(b
\fC PROCEDURE check (VAR ok: BOOLEAN); (* check order of a [1..n] *)\fP
\fC DECLARE\fP
\fC continue: BOOLEAN\fP
\fC BEGIN\fP
\fC IF test THEN write (1) ELSE skip END;\fP
\fC i := 1; continue := TRUE;\fP
\fC WHILE continue DO\fP
\fC IF i < n THEN\fP
\fC continue := NOT (a [i + 1] < a [i]);\fP
\fC IF continue THEN i := i + 1 ELSE skip END\fP
\fC ELSE\fP
\fC continue := FALSE\fP
\fC END\fP
\fC END;\fP
\fC ok := NOT (i < n)\fP
\fC END\fP
.)b
.(b
\fC BEGIN (* sort *)\fP
\fC i := 1;\fP
\fC WHILE i < n DO\fP
\fC write (1);\fP
\fC j := i; k := i;\fP
\fC WHILE j < n DO (* a [k] = MIN a [i..j] *)\fP
\fC j := j + 1;\fP
\fC IF a [j] < a [k] THEN k := j ELSE skip END\fP
\fC END;\fP
\fC h := a [i]; a [i] := a [k]; a [k] := h;\fP
\fC i := i + 1\fP
\fC END;\fP
\fC check (ok); WRITE (ok)\fP
\fC END\fP
.)b
.(b
\fCBEGIN (* main program *)\fP
\fC a [1] := 2.1415926536;\fP
\fC a [1] := a [1] + 1.0;\fP
\fC read (n, a);\fP
\fC sort (a);\fP
\fC IF NOT test THEN write (0) ELSE skip END\fP
\fCEND.\fP
.)b
.sz +2
.sh 1 ICode
.pp
The intermediate code (ICode) for MiniLAX is a subset of the intermediate
code for Pascal (P-Code)\*([.\*([[NAJ76\*(]]\*(.]
ICode programs consist of simple instructions
for a hypothetical computer \(em a stack machine.
.sh 2 "The ICode Machine"
.pp
The ICode Machine consists of three registers and memory.
The registers are
.(b
- PC the program counter
- SP the stack pointer
- AP the activation record pointer
.)b
.pp
The program counter points to the current instruction in the memory.
The stack pointer points to the highest occupied stack cell.
The activation record pointer points to the 'static link'
field of the current activation record.
.pp
The memory is divided in two parts, one containing the program
(Code) and the other containing data (Store). Code is an array
of ICode instructions.
Store is organized as stack (growing upwards)
which contains the data of the program executed.
Each activation of a procedure results in pushing an activation
record on the stack, which contains storage for parameters and
local data.
.pp
An activation record has the following layout:
.sp 0.5
.in +3
.PS
boxwid = 2.8
boxht = 0.7
down
A: box
B: box "- values of value parameters" "- addresses of reference parameters"
box ht 0.3 "return address"
C: box ht 0.3 "dynamic link"
D: box ht 0.3 "static link"
right
" store for local data" at A.e ljust
" store for parameters" at B.e ljust
" procedure call" at C.ne ljust
" information" at C.se ljust
move left from D.w
E: arrow to D.w
"AP" at E.w rjust
.PE
.in -3
.sp 1
.pp
At initialization time, the static and dynamic links and the return address
of the main program are all set to 0. The registers are initialized as
follows: PC := 0, SP := 3, and AP := 1.
The start address is 0, i.e. Code [0] contains
the first ICode instruction to be executed. PC is incremented before
the according instruction is executed. The interpreter stops at return
from the main program. The stop condition is: (PC = 0).
.pp
A procedure call enforces
.ip -
the creation of static and dynamic links of the new activation
record (ICode instruction: MST)
.ip -
parameter passing: The values of value parameters and the addresses
of reference parameters are evaluated and pushed on the stack.
.ip -
storing the return address and a jump to the procedure (ICode
instruction: JSR)
.ip -
reservation of store for local data of the new activation
record (ICode instruction: ENT)
.pp
A return from a procedure enforces
.ip -
discarding the current activation record by updating the registers
.sh 2 "ICode Instructions"
.pp
For each ICode instruction its operation code, its parameters
and its meaning is given in the following. The meaning is given
as text and as formula which describe operations
on the runtime stack. To simplify the description, within
formulas it is not taken care about the types of the stack elements.
.pp
If not further mentioned, the operations apply to the top
of the stack, which contains the actual element.
The following shorthand notations are used:
.sp
.nf
.ta 4
S runtime stack
base(P) returns a pointer to the P'th static predecessor of the current activation record
.fi
.sp
An instruction may have up to two parameters with the following meaning:
.nf
.sp 0.5
.ta 5
o offset
c, c1, c2 constants
a address (index of code section)
t indicates type integer (1), real (2) or boolean (3)
l block level difference between current and referenced activation record
.fi
.sp
Note: The types integer, real and boolean are encoded with 1, 2, and 3.
The boolean values FALSE and TRUE are encoded by 0 and 1.
.sp
1. Load instructions:
.sp 0.5
.ta 2.5 7 20
.nf
LDA l o SP:=SP+1; load address with base and offset
S[SP]:=base(l)+o;
.sp 0.2
LDC t c SP:=SP+1; load constant c of type t
S[SP]:=c;
.sp 0.2
LDI S[SP]:=S[S[SP]]; load indirect
.fi
.sp
2. Store instructions:
.sp 0.5
.nf
STI S[S[SP-1]]:=S[SP]; store into address contained
SP:=SP-1; in the element below the top
.fi
.sp
3. Jump instructions:
.sp 0.5
.nf
JMP a PC:=a; unconditional jump
.sp 0.2
FJP a if not S[SP] then PC:=a; conditional jump
SP:=SP-1;
.fi
4. Arithmetic instructions:
.sp 0.5
.nf
ADD t SP:=SP-1; addition of type t
S[SP]:=S[SP]+S[SP+1];
.sp 0.2
SUB SP:=SP-1; integer subtraction
S[SP]:=S[SP]-S[SP+1];
.sp 0.2
MUL t SP:=SP-1; multiplication of type t
S[SP]:=S[SP]*S[SP+1];
.fi
.sp
5. Logic instructions:
.sp 0.5
.nf
INV S[SP]:=not S[SP];
.sp 0.2
LES t SP:=SP-1; less operation of type t
S[SP]:=S[SP]<S[SP+1];
.fi
.sp
6. Address calculation instructions:
.sp 0.5
.nf
IXA c SP:=SP-1; compute indexed address
S[SP]:=c*S[SP+1]+S[SP];
.fi
.sp
7. Convert instructions:
.sp 0.5
.nf
FLT S[SP]:=real(S[SP]); converts from integer to real
.fi
.sp
8. Input-output instructions:
.sp 0.5
.nf
WRI t write(S[SP]); SP:=SP-1;
.sp 0.2
REA t SP:=SP+1; read(S[SP]);
.fi
.sp
9. Subroutine handling instructions:
.sp 0.5
.nf
MST l activation record initialization:
S[SP+1]:=base(l); - store static predecessor
S[SP+2]:=AP; - store dynamic predecessor
SP:=SP+3; return address (=S[SP+3]) is stored by JSR
.sp 0.2
JSR o a AP:=SP-(o+2); set AP to point to new activation record
o = #locations for parameters
S[AP+2]:=PC; store return address
PC:=a set PC to first instruction of subroutine
.sp 0.2
ENT o SP:=SP+o storage reservation for new block
o = length of local data segment
.sp 0.2
RET SP:=AP-1; return from subroutine:
PC:=S[SP+3]; - fetch return address to restore PC
AP:=S[SP+2]; - restore activation record pointer AP
.fi
.sp
10. check instructions:
.sp 0.5
.nf
CHK c1 c2 if (S[SP]<c1) or check against upper and lower bounds
(S[SP]>c2) then error
.fi
.if 0 \{\
.pp
Table 2 gives an overview of the parts of the specification and
the generated source modules. The sizes are given in terms of numbers of
lines. If two numbers are present the first one refers to the definition
module and the second one to the implementation module.
The complete specification is reproduced in Appendix B.
.(z
.ce
Table 2: Sizes of Specifications and Modules
.sp
.TS
box center tab (;);
l | c s | l | c s
l | r r | l | r r.
specification file;lines;module;lines
_
minilax.rex ; ;173;Scanner ; 48 +;753
; ; ;Source ; 42 +; 35
<library> ; ; ;Idents ; 55 +;169
<library> ; ; ;StringMem ; 49 +;126
minilax.lalr ; ;189;Parser ; 48 +;749
; ; ;Errors ; 35 +;224
minilax.cg ; ;435;Tree ;201 +;974
; ; ;Semantics ; 9 +;342
minilax.def ; ;129;Definitions;95 +;242
Types.md/mi ;53 +;168;Types ; 53 +;168
Coercions.md/mi ;28 +; 55;Coercions ; 28 +; 55
ICode.md/mi ; 7 +;122;ICode ; 7 +;122
ICodeInter.md/mi;22 +;342;ICodeInter; 22 +;342
minilax.mi ; ; 18;minilax ; ; 18
_
; ;1741; ; ;5011
.TE
.)z
.\}
.(z
.sp 0.5
.PS
define par #
box invis $1
line up boxht right boxwid * 1/6 from last box.sw
line right boxwid * 5/6
line down boxht left boxwid * 1/6
line left boxwid * 5/6
#
inch = 2.54
cm = 1 / inch
boxwid = 2.0 * cm
boxht = 1.5 * cm
dist = 0.5 * cm
delta = boxwid * 1/12
So: box "Source"
move right dist
Sc: par("Scanner")
move right dist at Sc.e
Pa: par("Parser")
move right dist at Pa.e
Tr: box "Tree"
move right dist
Ic: par("ICode")
move right dist at Ic.e
Ii: box "ICode-" "Inter"
move to Pa.n + (- boxwid/2, 2 * cm)
Ml: par("minilax")
Id: box "Idents" at Sc.s + (0, -2 * cm)
Er: box "Errors" at Pa.s + (0, -2 * cm)
move to Ic.s + (boxwid/2, -2 * cm)
Se: par("\s-2Semantics\s+2")
Sm: box "String-" "Memory" at Id.s + (0, -2 * cm)
Ty: box "Types" at Se.s + (0, -2 * cm)
De: box "Definitions" at Se.s + (- (dist + boxwid), -2 * cm)
arrow from So.e to Sc.w + (delta, 0)
arrow from Sc.e - (delta, 0) to Pa.w + (delta, 0)
arrow from Pa.e - (delta, 0) to Tr.w
arrow from Tr.e to Ic.w + (delta, 0)
arrow from Ic.e - (delta, 0) to Ii.w
arrow from Sc.s to Id.n <->
arrow from Id.s to Sm.n <->
arrow from Sc.s to Er.n
arrow from Pa.s to Er.n
arrow from Se.w + (delta, 0) to Er.e
arrow from Tr.s to Se.n <->
arrow from Se.s to De.n <->
arrow from Se.s to Ty.n <->
down
box "source" "file" invis at So.n + (0, 2 * cm)
arrow to So.n
box "input" "data" invis at Ii.n + (0, 2 * cm)
arrow to Ii.n
up
box "listing" invis at Er.s - (0, 2 * cm)
arrow to Er.s <-
box "output" "data" invis at Ii.s - (0, 2 * cm)
arrow to Ii.s <-
L1: box at So.s - (0, 7 * cm)
move to L1.s - (0, 2.0 * cm)
L2: par()
"data module (contains data and operations)" ljust at L1.e + (1 * cm, 0)
"function module (contains only operations)" ljust at L2.e + (1 * cm, 0)
.PE
.sp 2
.ce
Fig. 1: Module structure \- data flow (simplified)
.)z
.sh 1 "Compiler Structure"
.pp
Figure 1 gives an overview of the modules of the compiler.
.sh 2 Scanning
.pp
The scanner module as well as the source module are generated by the
scanner generator
.i Rex .
The source module provides access to the source file.
The scanner specification in the file
.i minilax.scan
contains only six rules describing comments, numbers, and identifiers. Except for comments,
a rule consists of a regular expression, an attribute computation, and a RETURN statement.
The rules for the other tokens including the keywords are extracted automatically from the
context-free grammar of the language and inserted at the line
.FT
INSERT RULES
.FR
using the preprocessors
.i "cg -xz"
and
.i rpp
\*([[Gro91c\*(]]. The source position of all tokens is
passed automatically in the variable
.i Attribute
to later compiler phases to be used for error messages.
.sh 2 Conversion
.pp
The three conversion operations used by the scanner are taken from a library
of reusable modules. The procedures
.i StringToInt
and
.i StringToReal
map strings to integer and real numbers. The procedure
.i MakeIdent
unambiguously maps identifiers (strings) to integer numbers using hashing
and a string memory.
.sh 2 Parsing
.pp
The parser module as well as the error reporting module are generated using
the LALR(1) parser generator
.i Lalr .
The parser specification in the file
.i minilax.pars
contains an LALR(1) grammar for MiniLAX.
As the subgrammar for expressions is ambiguous operator precedences are
given in the PREC section to solve this problem. The grammar is written in a
style that reflects the semantic structure of the language: Almost every
grammar rule corresponds to one node type in the abstract syntax tree.
.sh 2 "Tree Construction"
.pp
The tree module is generated by the generator for abstract syntax trees
.i Ast .
This module implements an abstract data type for trees. It primarily defines
the structure of the trees and provides procedures to construct the tree
nodes. The specification for the syntax tree is contained in the file
.i minilax.cg
(first page). It constitutes a context-free grammar augmented by several
features like rule names, selector names, typed attributes, and inheritance
which are described in detail in\*([<\*([[Gro91a\*(]]\*(>]. Each grammar rule corresponds to a
node type in the abstract syntax tree. For every node type a constructor procedure is
provided whose name consists of the rule name with the prefix 'm'.
.pp
The specification tries to keep the tree as small as possible.
The inheritance mechanism allows to avoid all chain rules. There are no
nodes for sequences of declarations, statements, etc.. Instead, every node
for a declaration or a statement has a field named
.i Next
describing the successor entity. Except for expressions, no separate nodes
are used for identifiers. The information is included as attribute in node types called
.i "Proc, Var, Formal" ,
and
.i Call .
The source position is stored only at the nodes where it might be needed
during semantic analysis. The above measures not only reduce the amount of
storage but they also reduce run time because less information has to be
produced and processed.
.pp
The mapping of the concrete syntax to the abstract syntax tree is specified
in the parser specification by the actions associated with the grammar
rules. The underlying principle is an attribute grammar with only one
synthesized attribute called
.i Tree .
Except for (semantic) chain rules, the action of every grammar rule
constructs one tree node by calling a generated constructor procedure.
.pp
For sequences of declarations, statements, etc. left recursive grammar rules
are used in order to avoid overflow of the parse stack. (This is historic
because the latest version of
.i Lalr
has a flexible stack which will never overflow.) This leads to syntax trees
where the elements of sequences are in the wrong order. The calls of the
procedure
.i ReverseTree
which is generated by
.i Ast
at certain places solves this problem.
.sh 2 "Semantic Analysis"
.pp
Semantic analysis which consists of name analysis, type checking, and
computation of code generation attributes is generated using the attribute
grammar generator
.i Ag
\*([[Gro89\*(]].
It generates evaluators for ordered attribute grammars. The attribute
computations have to be specified in a functional style. They are written in
the desired target language. They can use external functions of separately
compiled abstract data types.
.i Ag
cooperates with
.i Ast
in the sense that both tools understand the same specification language and
the generated attribute evaluators operate on the trees generated by
.i Ast .
.i Ag
offers many features which are improvements over traditional attribute
grammar systems like attributes local to rules, modules, tree-valued
attributes, and inheritance. Attributes of left-hand side symbols are
denoted just by the attribute name. For attributes of right-hand side symbols
the attribute name is preceded by the selector name of the symbol and a
colon.
.pp
The semantic analysis is specified by the attribute grammar in the file
.i minilax.cg .
The attribute grammar is based on the abstract syntax. It is divided into
modules where each module describes the computation of one attribute. The
context conditions are also contained in a separate module. The first page
of the specification describes the abstract syntax and the intrinsic
attributes whose values are supplied by the scanner and parser. The
attributes for semantic analysis are introduced in the individual modules.
.sh 2 "Name Analysis"
.pp
Name analysis is controlled by the modules
.i Decls
and
.i Env .
The attributes
.i DeclsIn
and
.i DeclsOut
collect the declarations in a scope. The attribute
.i Env
describes the environment information. It is distributed over all
declarations and statements where it is valid. Used identifiers are mapped
to the denoted objects by the procedure
.i Identify .
.pp
The data structure and procedures used in name analysis are provided by the
external module
.i Definitions .
This module is generated by
.i Ast
out of the specification in the file
.i Definitions.cg .
The last seven lines describe the data structure and the corresponding
constructor procedures. Some additional operations like e. g.
.i Identify
are provided as Modula-2 code.
.sh 2 "Operator Identification"
.pp
Operator identification for MiniLAX is trivial. It is handled in an ad hoc
manner in the module
.i TypeCode
by computing the attribute
.i TypeCode
for certain node types.
.sh 2 "Semantic Checks"
.pp
The attribute grammar modules
.i Formals
and
.i Types
control the type determination for every subexpression. The module
.i Conditions
contains all context checks for MiniLAX. The above modules use the
operations of the external module
.i Types
for manipulation of types. This module is conveniently generated by
.i Puma
\*([[Gro91b\*(]] out of a specification contained in the file
.i Types.puma .
The reporting of error messages is completely
expressed in the target language. The source position is treated like any
other attribute. This allows to combine error messages with precise source positions.
.sh 2 "Action Mapping"
.pp
The mapping of the abstract syntax tree to the intermediate language is
carried out in two steps. First, using the attribute grammar code generation
attributes are computed. Second, a transformation module generated by
.i Puma
\*([[Gro91b\*(]] traverses the attributed tree and emits the code.
.pp
The module
.i Co
computes for every subexpression an attribute describing the
coercions to be applied. Coercions are operations which are not explicitly
given in the source program. The procedure
.i Coerce
is imported from the external module
.i Types .
The module
.i DataSize
computes the offset of all variables.
The module
.i CodeSize
computes the code address of every target code instruction.
The module
.i Level
distributes the nesting level over the complete syntax tree.
The module
.i Label
copies the values of certain code addresses into local attributes. The
intention is to allow an optimizer to turn the attribute pair
.i CodeSizeIn/Out
into a global variable. The information gathered in the modules
.i "DataSize, CodeSize" ,
and
.i Level
is added to the definition table (see module
.i Decls )
in a functional way.
.pp
The program module
.i ICode
is generated by
.i Puma
\*([[Gro91b\*(]] out of the specification in the file
.i ICode.puma .
It recursively traverses the attributed tree and emits the intermediate code.
For this purpose it uses the operations of the hand-written module
.i ICodeInter
which stores the intermediate code in an array and provides an interpreter
and a printing routine for it. The reason for not using the attribute
grammar for code generation is the following:
The attribute grammar would compute the code as a list- or tree-valued
attribute of the root. As attribute grammars have no notion of output an
external function would have to transform this attribute value into the
array structure required by the interpreter. We save storage and runtime to
compute the code attribute by constructing the array directly.
Specifying the mapping to intermediate code using a separate module
is competitive to an attribute grammar version in terms of
clarity and size.
.pp
The module
.i minilax
is the main program that glues it all together.
.bp
.sh 1 "Specification"
.de FT
.)l
.sh 2 "\\$1"
.(l L
.ft C
.sz -4
..
.(l
.FT "minilax.scan"
EXPORT {
FROM Idents IMPORT tIdent;
FROM Positions IMPORT tPosition;
INSERT tScanAttribute
}
GLOBAL {
FROM SYSTEM IMPORT ADR;
FROM Strings IMPORT tString, StringToInt, StringToReal;
FROM Idents IMPORT tIdent, NoIdent, MakeIdent;
FROM Errors IMPORT Message, MessageI, Error, String;
INSERT ErrorAttribute
}
LOCAL { VAR Word: tString; }
DEFAULT {
GetWord (Word);
MessageI ("illegal character", Error, Attribute.Position, String, ADR (Word));
}
EOF {
IF yyStartState = Comment THEN
Message ("unclosed comment", Error, Attribute.Position);
END;
}
DEFINE digit = {0-9} .
letter = {a-z A-Z} .
START Comment
RULE
"(*" :- {yyStart (Comment);}
#Comment# "*)" :- {yyStart (STD);}
#Comment# "*" | - {*\\t\\n} + :- {}
#STD# digit + : {GetWord (Word);
Attribute.IntegerConst.Integer := StringToInt (Word);
RETURN IntegerConst;}
#STD# digit * "." digit + (E {+\\-} ? digit +) ?
: {GetWord (Word);
Attribute.RealConst.Real := StringToReal (Word);
RETURN RealConst;}
INSERT RULES #STD#
#STD# letter (letter | digit) *
: {GetWord (Word);
Attribute.Ident.Ident := MakeIdent (Word);
RETURN Ident;}
.FT "minilax.pars"
PARSER minilax
PREC LEFT '<' /* operator precedence */
LEFT '+'
LEFT '*'
LEFT NOT
PROPERTY INPUT
RULE /* concrete syntax */
Prog = PROGRAM Ident ';' 'DECLARE' Decls 'BEGIN' Stats 'END' '.' .
Decls = <
Decls1 = Decl .
Decls2 = Decls ';' Decl .
> .
Decl = <
Var = Ident ':' Type .
Proc0 = PROCEDURE Ident ';' 'DECLARE' Decls 'BEGIN' Stats 'END' .
Proc = PROCEDURE Ident '(' Formals ')' ';'
'DECLARE' Decls 'BEGIN' Stats 'END' .
> .
Formals = <
Formals1 = Formal .
Formals2 = Formals ';' Formal .
> .
Formal = <
Value = Ident ':' Type .
Ref = VAR Ident ':' Type .
> .
Type = <
Int = INTEGER .
Real = REAL .
Bool = BOOLEAN .
Array = ARRAY '[' Lwb: IntegerConst '..' Upb: IntegerConst ']' OF Type .
> .
Stats = <
Stats1 = Stat .
Stats2 = Stats ';' Stat .
> .
Stat = <
Assign = Adr ':=' Expr .
Call0 = Ident .
Call = Ident '(' Actuals ')' .
If = IF Expr THEN Then: Stats ELSE Else: Stats 'END' .
While = WHILE Expr DO Stats 'END' .
Read = READ '(' Adr ')' .
Write = WRITE '(' Expr ')' .
> .
Actuals = <
Expr1 = Expr .
Expr2 = Actuals ',' Expr .
> .
Expr = <
Less = Lop: Expr '<' Rop: Expr .
Plus = Lop: Expr '+' Rop: Expr .
Times = Lop: Expr '*' Rop: Expr .
Not = NOT Expr .
'()' = '(' Expr ')' .
IConst = IntegerConst .
RConst = RealConst .
False = FALSE .
True = TRUE .
Adr = <
Name = Ident .
Index = Adr '[' Expr ']' .
> .
> .
/* terminals (with attributes) */
Ident : [Ident: tIdent] { Ident := NoIdent ; } .
IntegerConst : [Integer ] { Integer := 0 ; } .
RealConst : [Real : REAL ] { Real := 0.0 ; } .
MODULE Tree
/* external functions for tree construction */
PARSER GLOBAL {
FROM Tree IMPORT
tTree , TreeRoot , ReverseTree , mMiniLax , mNoDecl ,
mDecl , mProc , mVar , mNoFormal , mFormal ,
mType , mInteger , mReal , mBoolean , mArray ,
mRef , mNoStat , mStat , mAssign , mCall ,
mIf , mWhile , mRead , mWrite , mNoActual ,
mActual , mExpr , mBinary , mUnary , mIntConst ,
mRealConst , mBoolConst , mAdr , mIndex , mIdent ,
Plus , Times , Less , Not , NoTree ;
VAR nInteger, nReal, nBoolean : tTree;
}
BEGIN {
nInteger := mInteger ();
nReal := mReal ();
nBoolean := mBoolean ();
}
/* attributes for tree construction */
DECLARE
Decls Decl Formals Formal Type Stats Stat Actuals Expr = [Tree: tTree] .
RULE /* tree construction = */
/* mapping: concrete syntax -> abstract syntax */
Prog = { => {TreeRoot := mMiniLax (mProc (mNoDecl (), Ident:Ident,
Ident:Position, mNoFormal (), ReverseTree (Decls:Tree),
ReverseTree (Stats:Tree)));}; } .
Decls1 = { Tree := {Decl:Tree^.\\Decl.Next := mNoDecl (); Tree := Decl:Tree;}; } .
Decls2 = { Tree := {Decl:Tree^.\\Decl.Next := Decls:Tree; Tree := Decl:Tree;}; } .
Var = { Tree := mVar (NoTree, Ident:Ident, Ident:Position, mRef (Type:Tree));}.
Proc0 = { Tree := mProc (NoTree, Ident:Ident, Ident:Position, mNoFormal (),
ReverseTree (Decls:Tree), ReverseTree (Stats:Tree)); } .
Proc = { Tree := mProc (NoTree, Ident:Ident, Ident:Position, ReverseTree
(Formals:Tree), ReverseTree (Decls:Tree), ReverseTree (Stats:Tree)); } .
Formals1= { Tree := {Formal:Tree^.\\Formal.Next := mNoFormal ();
Tree := Formal:Tree;}; } .
Formals2= { Tree := {Formal:Tree^.\\Formal.Next := Formals:Tree;
Tree := Formal:Tree;}; } .
Value = { Tree := mFormal (NoTree, Ident:Ident, Ident:Position,
mRef (Type:Tree)); } .
Ref = { Tree := mFormal (NoTree, Ident:Ident, Ident:Position,
mRef (mRef (Type:Tree))); } .
Int = { Tree := nInteger; } .
Real = { Tree := nReal; } .
Bool = { Tree := nBoolean; } .
Array = { Tree := mArray (Type:Tree, Lwb:Integer, Upb:Integer, Lwb:Position); } .
Stats1 = { Tree := {Stat:Tree^.\\Stat.Next := mNoStat (); Tree := Stat:Tree;}; } .
Stats2 = { Tree := {Stat:Tree^.\\Stat.Next := Stats:Tree; Tree := Stat:Tree;}; } .
Assign = { Tree := mAssign (NoTree, Adr:Tree, Expr:Tree, ':=':Position); } .
Call0 = { Tree := mCall (NoTree, mNoActual (Ident:Position), Ident:Ident,
Ident:Position); } .
Call = { Tree := mCall (NoTree, ReverseTree (Actuals:Tree), Ident:Ident,
Ident:Position); } .
If = { Tree := mIf (NoTree, Expr:Tree, ReverseTree (Then:Tree),
ReverseTree (Else:Tree)); } .
While = { Tree := mWhile (NoTree, Expr:Tree, ReverseTree (Stats:Tree)); } .
Read = { Tree := mRead (NoTree, Adr:Tree); } .
Write = { Tree := mWrite (NoTree, Expr:Tree); } .
Expr1 = { Tree := mActual (mNoActual (Expr:Tree^.\\Expr.Pos), Expr:Tree); } .
Expr2 = { Tree := mActual (Actuals:Tree, Expr:Tree); } .
Less = { Tree := mBinary ('<':Position, Lop:Tree, Rop:Tree, Less); } .
Plus = { Tree := mBinary ('+':Position, Lop:Tree, Rop:Tree, Plus); } .
Times = { Tree := mBinary ('*':Position, Lop:Tree, Rop:Tree, Times); } .
Not = { Tree := mUnary (NOT:Position, Expr:Tree, Not); } .
IConst = { Tree := mIntConst (IntegerConst:Position, IntegerConst:Integer); } .
RConst = { Tree := mRealConst (RealConst:Position, RealConst:Real); } .
False = { Tree := mBoolConst (FALSE:Position, \\FALSE); } .
True = { Tree := mBoolConst (TRUE:Position, \\TRUE); } .
Name = { Tree := mIdent (Ident:Position, Ident:Ident); } .
Index = { Tree := mIndex ('[':Position, Adr:Tree, Expr:Tree); } .
END Tree
.FT "minilax.cg"
MODULE AbstractSyntax /* ------------------------------------------ */
TREE IMPORT {
FROM Idents IMPORT tIdent;
FROM Positions IMPORT tPosition;
}
GLOBAL {
FROM Idents IMPORT tIdent;
FROM Positions IMPORT tPosition;
}
EVAL Semantics
PROPERTY INPUT
RULE
MiniLAX = Proc .
Decls = <
NoDecl = .
Decl = Next: Decls REV [Ident: tIdent] [Pos: tPosition] <
Var = Type .
Proc = Formals Decls Stats .
>.
>.
Formals = <
NoFormal = .
Formal = Next: Formals REV [Ident: tIdent] [Pos: tPosition] Type .
>.
Type = <
Integer = .
Real = .
Boolean = .
Array = Type OUT [Lwb] [Upb] [Pos: tPosition] .
Ref = Type OUT .
NoType = .
ErrorType = .
>.
Stats = <
NoStat = .
Stat = Next: Stats REV <
Assign = Adr Expr [Pos: tPosition] .
Call = Actuals [Ident: tIdent] [Pos: tPosition] .
If = Expr Then: Stats Else: Stats .
While = Expr Stats .
Read = Adr .
Write = Expr .
>.
>.
Actuals = <
NoActual = [Pos: tPosition OUT] .
Actual = Next: Actuals REV Expr .
>.
Expr = [Pos: tPosition] <
Binary = Lop: Expr Rop: Expr [Operator: SHORTCARD] .
Unary = Expr [Operator: SHORTCARD] .
IntConst = [Value OUT] .
RealConst = [Value: REAL OUT] .
BoolConst = [Value: BOOLEAN OUT] .
Adr = <
Index = Adr Expr .
Ident = [Ident: tIdent] .
>.
>.
Coercions = <
NoCoercion = .
Coercion = Next: Coercions OUT <
Content = . /* fetch contents of location */
IntToReal = . /* convert integer value to real */
>.
>.
END AbstractSyntax
MODULE Output /* -------------------------------------------------- */
PROPERTY OUTPUT
DECLARE
Formals Decls = [Decls: tObjects THREAD] .
Call Ident = [Object: tObjects] [level: SHORTINT] .
If While = [Label1] [Label2] .
Read Write Binary = [TypeCode: SHORTCARD] .
Expr = Type Co: Coercions .
Index = type: Type .
END Output
MODULE Decls /* -------------------------------------------------- */
EVAL GLOBAL { FROM Definitions IMPORT mNoObject, mProc, mVar, mProc2, mVar2, Identify; }
DECLARE Formal Decl = [Object: tvoid OUT] .
RULE
MiniLAX = { Proc: DeclsIn := nNoObject ; } .
Decl = { Next: DeclsIn := nNoObject ;
DeclsOut:= Next: DeclsOut ;
Object := {} ; } .
Proc = { Next: DeclsIn := mProc (DeclsIn, Ident, Formals) ;
Object := {mProc2 (Next:DeclsIn, Level, CodeSizeIn,
Formals:DataSizeOut, Decls:DataSizeOut);};
Formals: DeclsIn := nNoObject ; } .
Var = { Next: DeclsIn := mVar (DeclsIn, Ident, Type) ;
Object := {mVar2 (Next:DeclsIn, Level, DataSizeIn);}; } .
Formal = { Next: DeclsIn := mVar (DeclsIn, Ident, Type) ;
Object := {mVar2 (Next:DeclsIn, Level, DataSizeIn);}; } .
Call = { Object := Identify (Ident, Env) ; } .
Ident = { Object := Identify (Ident, Env) ; } .
END Decls
MODULE Formals /* -------------------------------------------------- */
EVAL GLOBAL {
FROM Definitions IMPORT tObjects, GetFormals;
FROM Tree IMPORT Formal;
FROM Types IMPORT CheckParams;
}
DECLARE Actuals = [Formals: MyTree] .
RULE
Call = { Actuals: Formals := GetFormals (Object) ;
=> { CheckParams (Actuals, Actuals:Formals); } ; } .
Actual = { Next: Formals := {IF Formals^.Kind = Formal
THEN Next:Formals := Formals^.Formal.\\Next
ELSE Next:Formals := Formals;
END;} ; } .
END Formals
MODULE Env /* -------------------------------------------------- */
EVAL GLOBAL { FROM Definitions IMPORT tEnv, NoEnv, mEnv; }
DECLARE Decls Stats Actuals Expr = [Env: tEnv INH] .
RULE
MiniLAX = { Proc: Env := NoEnv ; } .
Proc = { Stats: Env := mEnv (Decls:DeclsOut, Env) ;
Decls: Env := Stats: Env ; } .
END Env
MODULE Type /* -------------------------------------------------- */
EVAL GLOBAL {
FROM Definitions IMPORT GetType;
FROM Types IMPORT GetElementType, Reduce, ResultType;
FROM Tree IMPORT tTree, mBoolean, mInteger, mReal, mRef, mNoType, mNoCoercion;
}
RULE
Expr = { Type := nNoType ; } .
Binary = { Type := ResultType (Lop:Type, Rop:Type, Operator); } .
Unary = { Type := ResultType (Expr:Type, nNoType, Operator); } .
IntConst = { Type := nInteger ; } .
RealConst = { Type := nReal ; } .
BoolConst = { Type := nBoolean ; } .
Adr = { Type := nNoType ; } .
Index = { Type := mRef (GetElementType (type)) ;
type := Reduce (Adr:Type) ; } .
Ident = { Type := GetType (Object) ; } .
END Type
MODULE TypeCode /* -------------------------------------------------- */
EVAL GLOBAL { FROM ICodeInter IMPORT IntType, RealType, BoolType; }
DECLARE Read Write Binary = [type: tTree] .
Read = { type := Reduce (Adr:Type) ;
TypeCode := ICodeType [type^.Kind] ; } .
Write = { type := Reduce (Expr:Type) ;
TypeCode := ICodeType [type^.Kind] ; } .
Binary = { type := Reduce (Rop:Type) ;
TypeCode := ICodeType [type^.Kind] ; } .
END TypeCode
MODULE Co /* -------------------------------------------------- */
EVAL GLOBAL { FROM Types IMPORT Reduce1, ReduceToRef, Coerce; }
RULE
Assign = { Adr : Co := Coerce (Adr :Type, ReduceToRef (Adr:Type));
Expr: Co := Coerce (Expr:Type, Reduce (Adr:Type)) ; } .
If = { Expr: Co := Coerce (Expr:Type, Reduce (Expr:Type)) ; } .
While = { Expr: Co := Coerce (Expr:Type, Reduce (Expr:Type)) ; } .
Read = { Adr : Co := Coerce (Adr :Type, ReduceToRef (Adr:Type)); } .
Write = { Expr: Co := Coerce (Expr:Type, Reduce (Expr:Type)) ; } .
Actual = { Expr: Co := {
IF Formals^.Kind = NoFormal
THEN Expr:Co := mNoCoercion ();
ELSE Expr:Co := Coerce (Expr:Type, Reduce1 (Formals^.Formal.Type));
END; } ; } .
Binary = { Lop : Co := Coerce (Lop :Type, Reduce (Lop:Type)) ;
Rop : Co := Coerce (Rop :Type, Reduce (Rop:Type)) ; } .
Unary = { Expr: Co := Coerce (Expr:Type, Reduce (Expr:Type)) ; } .
Index = { Adr : Co := Coerce (Adr :Type, ReduceToRef (Adr:Type));
Expr: Co := Coerce (Expr:Type, Reduce (Expr:Type)) ; } .
END Co
MODULE DataSize /* -------------------------------------------------- */
EVAL GLOBAL { FROM Types IMPORT TypeSize; }
DECLARE Decls Formals = [DataSize THREAD] .
RULE
MiniLAX = { Proc: DataSizeIn := 0 ; } .
Decl = { DataSizeOut := Next: DataSizeOut ; } .
Proc = { Formals: DataSizeIn := 3 ; } .
Var = { Next: DataSizeIn := DataSizeIn + TypeSize (Reduce1 (Type)); } .
Formal = { Next: DataSizeIn := DataSizeIn + 1 ; } .
END DataSize
MODULE CodeSize /* -------------------------------------------------- */
DECLARE Decls Stats Actuals Expr = [CodeSize THREAD] .
Expr Coercions = [CoercionSize SYN] .
RUL
MiniLAX = { Proc: CodeSizeIn := 0 ; } .
Decl = { CodeSizeOut := Next: CodeSizeOut ; } .
Proc = { Stats:CodeSizeIn := CodeSizeIn +1 ; /* ENT */
Decls:CodeSizeIn := Stats:CodeSizeOut+1 ; /* RET */
Next: CodeSizeIn := Decls:CodeSizeOut ; } .
Stat = { CodeSizeOut := Next: CodeSizeOut ; } .
Assign = { Adr: CodeSizeIn := CodeSizeIn ;
Expr: CodeSizeIn := Adr: CodeSizeOut+Adr:CoercionSize;
Next: CodeSizeIn := Expr: CodeSizeOut+Expr:CoercionSize+1; /* STI */ } .
Call = { Actuals:CodeSizeIn:= CodeSizeIn+1 ; /* MST */
Next: CodeSizeIn := Actuals:CodeSizeOut+1; /* JSR */ } .
If = { Expr: CodeSizeIn := CodeSizeIn ;
Then: CodeSizeIn := Expr: CodeSizeOut+Expr:CoercionSize+1; /* FJP */
Else: CodeSizeIn := Then: CodeSizeOut+1 ; /* JMP */
Next: CodeSizeIn := Else: CodeSizeOut ; } .
While = { Stats:CodeSizeIn := CodeSizeIn +1 ; /* JMP */
Expr: CodeSizeIn := Stats:CodeSizeOut ;
Next: CodeSizeIn := Expr: CodeSizeOut+Expr:CoercionSize+2;
/* INV, FJP */ } .
Read = { Adr: CodeSizeIn := CodeSizeIn ;
Next: CodeSizeIn := Adr: CodeSizeOut+Adr:CoercionSize+2;
/* REA, STI */ } .
Write = { Expr: CodeSizeIn := CodeSizeIn ;
Next: CodeSizeIn := Expr: CodeSizeOut+Expr:CoercionSize+1; /* WRI */ } .
Actual = { Expr: CodeSizeIn := CodeSizeIn ;
Next: CodeSizeIn := Expr: CodeSizeOut+Expr:CoercionSize;
CodeSizeOut := Next: CodeSizeOut ; } .
Binary = { Rop: CodeSizeIn := Lop: CodeSizeOut+Lop:CoercionSize;
CodeSizeOut := Rop: CodeSizeOut+Rop:CoercionSize+1;
/* INV, MUL, ADD or LES */ } .
Unary = { CodeSizeOut := Expr: CodeSizeOut+Expr:CoercionSize+1; /* NOT */ } .
IntConst = { CodeSizeOut := CodeSizeIn+1 ; /* LDC */ } .
RealConst = { CodeSizeOut := CodeSizeIn+1 ; /* LDC */ } .
BoolConst = { CodeSizeOut := CodeSizeIn+1 ; /* LDC */ } .
Index = { Expr:CodeSizeIn := Adr: CodeSizeOut+Adr:CoercionSize;
CodeSizeOut := Expr: CodeSizeOut+Expr:CoercionSize+4;
/* CHK, LDC, SUB, IXA */ } .
Ident = { CodeSizeOut := CodeSizeIn+1 ; /* LDA */ } .
Expr = { CoercionSize:= Co: CoercionSize ; } .
Coercions = { CoercionSize:= 0 ; } .
Content = { CoercionSize:= Next: CoercionSize+1; /* LDI */ } .
IntToReal = { CoercionSize:= Next: CoercionSize+1; /* FLT */ } .
END CodeSize
MODULE Level /* -------------------------------------------------- */
DECLARE Decls Formals Stats Actuals Expr = [Level: SHORTINT INH] .
RULE
MiniLAX = { Proc: Level := 0 ; } .
Proc = { Formals: Level := Level + 1 ;
Decls: Level := Formals: Level ;
Stats: Level := Formals: Level ; } .
Call = { level := Level ; } .
Ident = { level := Level ; } .
END Level
MODULE Label /* -------------------------------------------------- */
RULE
If = { Label1 := Else: CodeSizeIn ;
Label2 := Else: CodeSizeOut ; } .
While = { Label1 := Stats: CodeSizeIn ;
Label2 := Expr: CodeSizeIn ; } .
END Label
MODULE Conditions /* -------------------------------------------------- */
EVAL GLOBAL {
FROM Definitions IMPORT IsDeclared, IsObjectKind, NoObject, Proc, Var;
FROM Tree IMPORT Integer, Boolean, Array, ErrorType, NoFormal, IsType, Error;
FROM Types IMPORT IsAssignmentCompatible, IsSimpleType;
}
RULE
Decl = { CHECK NOT IsDeclared (Ident, DeclsIn)
==> Error ("identifier already declared" , Pos) ; } .
Formal = { CHECK NOT IsDeclared (Ident, DeclsIn)
==> Error ("identifier already declared" , Pos) ;
CHECK IsSimpleType (Reduce1 (Type))
==> Error ("value parameter must have simple type", Pos) ; } .
Array = { CHECK Lwb <= Upb
==> Error ("lower bound exceeds upper bound" , Pos) ; } .
Assign = { CHECK IsAssignmentCompatible (Adr:Type, Expr:Type)
==> Error ("types not assignment compatible" , Pos) ; } .
Call = { CHECK Object^.Kind # NoObject
==> Error ("identifier not declared" , Pos) ;
CHECK IsObjectKind (Object, Proc)
==> Error ("only procedures can be called" , Pos) ; } .
If = { CHECK IsType (Reduce (Expr:Type), Boolean)
==> Error ("boolean expression required" , Expr:Pos) ; } .
While = { CHECK IsType (Reduce (Expr:Type), Boolean)
==> Error ("boolean expression required" , Expr:Pos) ; } .
Read = { CHECK IsSimpleType (Reduce (Adr:Type))
==> Error ("simple type operand required" , Adr:Pos) ; } .
Write = { CHECK IsSimpleType (Reduce (Expr:Type))
==> Error ("simple type operand required" , Expr:Pos) ; } .
Binary = { CHECK Type^.Kind # ErrorType
==> Error ("operand types incompatible" , Pos) ; } .
Unary = { CHECK Type^.Kind # ErrorType
==> Error ("operand types incompatible" , Pos) ; } .
Index = { CHECK IsType (Reduce (Adr:Type), Array)
==> Error ("only arrays can be indexed" , Adr:Pos) ;
CHECK IsType (Reduce (Expr:Type), Integer)
==> Error ("integer expression required" , Expr:Pos) ; } .
Ident = { CHECK Object^.Kind # NoObject
==> Error ("identifier not declared" , Pos) ;
CHECK IsObjectKind (Object, Var)
==> Error ("variable required" , Pos) ; } .
END Conditions
MODULE TypeDecls /* -------------------------------------------------- */
TREE IMPORT {
FROM SYSTEM IMPORT ADDRESS;
FROM Definitions IMPORT tObjects, tEnv;
PROCEDURE Error (Text: ARRAY OF CHAR; Position: tPosition);
TYPE tvoid = RECORD END;
CONST
Plus = 1;
Times = 2;
Less = 3;
Not = 4;
}
EXPORT { TYPE MyTree = tTree; }
GLOBAL {
FROM Strings IMPORT tString, ArrayToString;
IMPORT Errors;
ROCEDURE Error (Text: ARRAY OF CHAR; Position: tPosition);
BEGIN
Errors.Message (Text, Errors.Error, Position);
END Error;
}
EVAL GLOBAL {
TYPE MyTree = Tree.tTree;
VAR nNoObject : tObjects;
VAR nInteger, nReal, nBoolean, nNoType : tTree;
VAR ICodeType : ARRAY [Integer .. Boolean] OF [IntType .. BoolType];
}
BEGIN {
nNoObject := mNoObject ();
nInteger := mInteger ();
nReal := mReal ();
nBoolean := mBoolean ();
nNoType := mNoType ();
ICodeType [Tree.Integer ] := IntType ;
ICodeType [Tree.Real ] := RealType ;
ICodeType [Tree.Boolean ] := BoolType ;
}
END TypeDecls
.FT "Definitions.cg"
TREE Definitions
IMPORT {
FROM Idents IMPORT tIdent;
FROM SYSTEM IMPORT ADDRESS;
}
EXPORT {
CONST NoEnv = NoDefinitions;
TYPE
tObjects = tDefinitions; (* type to represent sets of objects *)
tEnv = tDefinitions; (* type to represent environments *)
PROCEDURE Identify (Ident: tIdent; Env: tEnv): tObjects;
(* return the object associated with 'Ident' in *)
(* the environment 'Env' *)
PROCEDURE IsDeclared (Ident: tIdent; Objects: tObjects): BOOLEAN;
(* check whether an object having the name *)
(* 'Ident' is element of the set of objects *)
(* 'Objects' *)
PROCEDURE mProc2 (Object: tObjects; Level, Label, ParSize, DataSize: INTEGER);
(* extend the description 'Object' of a *)
(* procedure by the 4 given attributes *)
PROCEDURE mVar2 (Object: tObjects; Level, Offset: INTEGER);
(* extend the description 'Object' of a *)
(* variable by the 2 given attributes *)
PROCEDURE IsObjectKind (Object: tObjects; Kind: SHORTCARD): BOOLEAN;
(* returns TRUE if the kind of the 'Object' *)
(* is equal to parameter 'Kind' *)
PROCEDURE GetFormals (Object: tObjects): ADDRESS;
(* returns the list of formal parameters *)
(* from the description 'Object' of a procedure *)
PROCEDURE GetType (Object: tObjects): ADDRESS;
(* returns the type of the description 'Object' *)
(* of a variable *)
}
GLOBAL {
FROM Idents IMPORT tIdent;
FROM SYSTEM IMPORT ADDRESS;
FROM Tree IMPORT mNoType, mNoFormal;
VAR nNoObject : tObjects;
PROCEDURE IsDeclared (Ident: tIdent; Objects: tObjects): BOOLEAN;
BEGIN
WHILE Objects^.Kind # NoObject DO
IF Objects^.Object.Ident = Ident THEN
RETURN TRUE;
END;
Objects := Objects^.Object.Next;
END;
RETURN FALSE;
END IsDeclared;
PROCEDURE Identify (Ident: tIdent; Env: tEnv): tObjects;
VAR Objects : tObjects;
BEGIN
WHILE Env # NoEnv DO
Objects := Env^.Env.Objects;
WHILE Objects^.Kind # NoObject DO
IF Objects^.Object.Ident = Ident THEN
RETURN Objects;
END;
Objects := Objects^.Object.Next;
END;
Env := Env^.Env.Hidden;
END;
RETURN nNoObject;
END Identify;
PROCEDURE mProc2 (Object: tObjects; Level, Label, ParSize, DataSize: INTEGER);
BEGIN
Object^.Proc.Level := Level;
Object^.Proc.Label := Label;
Object^.Proc.ParSize := ParSize;
Object^.Proc.DataSize := DataSize;
END mProc2;
PROCEDURE mVar2 (Object: tObjects; Level, Offset: INTEGER);
BEGIN
Object^.Var.Level := Level;
Object^.Var.Offset := Offset;
END mVar2;
PROCEDURE IsObjectKind (Object: tObjects; Kind: SHORTCARD): BOOLEAN;
BEGIN
RETURN (Object^.Kind = Kind) OR (Object^.Kind = NoObject);
END IsObjectKind;
PROCEDURE GetFormals (Object: tObjects): ADDRESS;
BEGIN
IF Object^.Kind = Proc THEN
RETURN Object^.Proc.Formals;
ELSE
RETURN mNoFormal ();
END;
END GetFormals;
PROCEDURE GetType (Object: tObjects): ADDRESS;
BEGIN
IF Object^.Kind = Var THEN
RETURN Object^.Var.Type;
ELSE
RETURN mNoType ();
END;
END GetType;
}
BEGIN { nNoObject := mNoObject (); }
RULE
Env = Objects Hidden: Env .
Objects = <
NoObject = .
Object = Next: Objects [Ident: tIdent] <
Proc = [Formals: ADDRESS] -> [Level: SHORTINT] [Label] [ParSize] [DataSize] .
Var = [Type: ADDRESS] -> [Level: SHORTINT] [Offset] .
> .
> .
.FT "Types.puma"
TRAFO Types PUBLIC
Reduce /* return type without any ref levels */
ReduceToRef /* return type with ref level 1 */
Reduce1 /* return type with 1 ref level removed */
RefLevel /* return number of ref levels of a type */
IsSimpleType /* check whether a type is simple */
IsCompatible /* check whether two types are compatible */
IsAssignmentCompatible /* check whether two types are */
/* assignment compatible */
ResultType /* return the type of the result of */
/* applying an operator to two operands */
CheckParams /* check a formal list of parameters */
/* against an actual list of parameters */
GetElementType /* return the type of the elements of */
/* an array type */
TypeSize /* return the number of bytes used for */
/* the internal representation of an */
/* object of a certain type */
Coerce /* returns the coercion necessary to convert */
/* an object of type 't1' to type 't2' */
EXTERN Error
GLOBAL {
FROM Positions IMPORT tPosition;
FROM Tree IMPORT
tTree , Array , Ref , NoType ,
Plus , Times , Less , Not ,
mBoolean , mNoType , mNoCoercion , Error ;
VAR nBoolean, nNoType, nNoCoercion : tTree;
}
BEGIN {
nBoolean := mBoolean ();
nNoType := mNoType ();
nNoCoercion := mNoCoercion ();
}
FUNCTION Reduce (Type) Type
Ref (t) RETURN Reduce (t) ?.
t RETURN t ?.
FUNCTION ReduceToRef (Type) Type
Ref (t:Ref) RETURN ReduceToRef (t) ?.
t:Ref RETURN t ?.
t RETURN t ?.
FUNCTION Reduce1 (Type) Type
Ref (t) RETURN t ?.
t RETURN t ?.
FUNCTION RefLevel (Type) INTEGER
Ref (t) RETURN RefLevel (t) + 1 ?.
_ RETURN 0 ?.
PREDICATE IsSimpleType (Type)
Array ? FAIL; .
_ ?.
PREDICATE IsCompatible (Type, Type)
Integer , Integer ?.
Real , Real ?.
Boolean , Boolean ?.
Array (t1, Lwb, Upb, _), Array (t2, Lwb, Upb, _) ;
Ref (t1) , t2 ;
t1 , Ref (t2) ? IsCompatible (t1, t2); .
NoType , _ ?.
_ , NoType ?.
ErrorType , _ ?.
_ , ErrorType ?.
PREDICATE IsAssignmentCompatible (Type, Type)
Integer , Integer ?.
Real , Real ?.
Real , Integer ?.
Boolean , Boolean ?.
Ref (t1) , t2 ;
t1 , Ref (t2) ? IsAssignmentCompatible (t1, t2); .
NoType , _ ?.
_ , NoType ?.
ErrorType , _ ?.
_ , ErrorType ?.
FUNCTION ResultType (Type, Type, INTEGER) Type EXTERN Plus Times Less Not nBoolean;
t:Integer , Integer , { Plus } RETURN t ?.
t:Real , Real , { Plus } RETURN t ?.
t:Integer , Integer , { Times } RETURN t ?.
t:Real , Real , { Times } RETURN t ?.
Integer , Integer , { Less } RETURN nBoolean ?.
Real , Real , { Less } RETURN nBoolean ?.
t:Boolean , Boolean , { Less } RETURN t ?.
t:Boolean , _ , { Not } RETURN t ?.
Ref (t1) , t2 , o ;
t1 , Ref (t2) , o RETURN ResultType (t1, t2, o) ?.
t:NoType , _ , _ RETURN t ?.
_ , t:NoType , _ RETURN t ?.
ErrorType , _ , _ RETURN NoType ?.
_ , ErrorType , _ RETURN NoType ?.
.. RETURN ErrorType?.
PROCEDURE CheckParams (Actuals, Formals)
NoActual , NoFormal ?.
NoActual (Pos), _ ?
Error ("too few actual parameters" , Pos); .
Actual (_, Expr (Pos, ..)), NoFormal ?
Error ("too many actual parameters" , Pos); .
/* alternative 1 */
Actual (NextA, Expr (Pos, TypeA, ..)), Formal (_, _, NextF, _, _, TypeF) ?
{
IF NOT IsCompatible (TypeA, TypeF) THEN
Error ("parameter type incompatible", Pos);
END;
IF NOT (RefLevel (TypeF) - 1 <= RefLevel (TypeA)) THEN
Error ("variable required" , Pos);
END;
};
CheckParams (NextA, NextF); .
/* alternative 2 */
Actual (NextA, Expr (Pos, TypeA, ..)), Formal (_, _, NextF, _, _, TypeF) ?
NOT IsCompatible (TypeA, TypeF);
Error ("parameter type incompatible" , Pos);
REJECT; .
Actual (NextA, Expr (Pos, TypeA, ..)), Formal (_, _, NextF, _, _, TypeF) ?
NOT (RefLevel (TypeF) - 1 <= RefLevel (TypeA));
Error ("variable required" , Pos);
REJECT; .
Actual (NextA, Expr (Pos, TypeA, ..)), Formal (_, _, NextF, _, _, TypeF) ?
CheckParams (NextA, NextF); .
/* alternative 3 */
Actual (NextA, Expr (Pos, TypeA, ..)), Formal (_, _, NextF, _, _, TypeF) ?
CheckCompatible (Pos, TypeA, TypeF);
CheckRefLevel (Pos, TypeA, TypeF);
CheckParams (NextA, NextF); .
PROCEDURE CheckCompatible (tPosition, Type, Type)
_ , t1 , t2 ? IsCompatible (t1, t2); .
Pos , .. ? Error ("parameter type incompatible" , Pos); .
PROCEDURE CheckRefLevel (tPosition, Type, Type)
_ , t1 , t2 ? RefLevel (t2) - 1 <= RefLevel (t1); .
Pos , .. ? Error ("variable required" , Pos); .
FUNCTION GetElementType (Type) Type
Array (t, ..) RETURN t ?.
_ RETURN NoType ?.
FUNCTION TypeSize (Type) INTEGER
Array (t, Lwb, Upb, _) RETURN (Upb - Lwb + 1) * TypeSize (t) ?.
_ RETURN 1 ?.
FUNCTION Coerce (t1: Type, t2: Type) Coercions EXTERN nNoCoercion;
Ref (T1) , Ref (T2) RETURN Coerce (T1, T2) ?.
Integer , Real RETURN IntToReal (nNoCoercion) ?.
Ref (T1) , T2 RETURN Content (Coerce (T1, T2)) ?.
.. RETURN nNoCoercion ?.
.FT "ICode.puma"
TRAFO ICode TREE Tree Definitions PUBLIC Code
EXTERN
ADD BoolType CHK ENT Emit EmitReal FJP FLT FalseCode INV IXA IntType JMP JSR
LDA LDC LDI LES MST MUL REA RET RealType STI SUB TrueCode TypeSize WRI
GLOBAL {
FROM Tree IMPORT tTree, Times, Plus, Less;
FROM Definitions IMPORT tObjects;
FROM Types IMPORT TypeSize;
FROM ICodeInter IMPORT
IntType , RealType , BoolType , OpCode ,
Emit , EmitReal , TrueCode , FalseCode ;
}
PROCEDURE Code (t: Tree)
MiniLax (Proc) ?
Code (Proc);
.
Proc (Next := Next:Decls (Definitions.Proc (ParSize := ParSize,
DataSize := DataSize), ..), Decls := Decls, Stats := Stats) ?
Emit (ENT, DataSize - ParSize, 0);
Code (Stats);
Emit (RET, 0, 0);
Code (Decls);
Code (Next);
.
Var (Next := Next) ?
Code (Next);
.
Assign (Next, Adr, Expr, _) ?
Code (Adr); Code (Adr::Co);
Code (Expr); Code (Expr::Co);
Emit (STI, 0, 0);
Code (Next);
.
Call (Next, Actuals, _, _, Definitions.Proc (Level := Level, Label := Label,
ParSize := ParSize), level) ?
Emit (MST, level - Level, 0);
Code (Actuals);
Emit (JSR, ParSize - 3, Label);
Code (Next);
.
If (Next, Expr, Then, Else, Label1, Label2) ?
Code (Expr); Code (Expr::Co);
Emit (FJP, Label1, 0);
Code (Then);
Emit (JMP, Label2, 0);
Code (Else);
Code (Next);
.
While (Next, Expr, Stats, Label1, Label2) ?
Emit (JMP, Label2, 0);
Code (Stats);
Code (Expr); Code (Expr::Co);
Emit (INV, 0, 0);
Emit (FJP, Label1, 0);
Code (Next);
.
Read (Next, Adr, TypeCode) ?
Code (Adr); Code (Adr::Co);
Emit (REA, TypeCode, 0);
Emit (STI, 0, 0);
Code (Next);
.
Write (Next, Expr, TypeCode) ?
Code (Expr); Code (Expr::Co);
Emit (WRI, TypeCode, 0);
Code (Next);
.
Actual (Next, Expr) ?
Code (Expr); Code (Expr::Co);
Code (Next);
.
Binary (_, _, _, Lop, Rop, {Times}, TypeCode) ?
Code (Lop); Code (Lop::Co);
Code (Rop); Code (Rop::Co);
Emit (MUL, TypeCode, 0);
.
Binary (_, _, _, Lop, Rop, {Plus}, TypeCode) ?
Code (Lop); Code (Lop::Co);
Code (Rop); Code (Rop::Co);
Emit (ADD, TypeCode, 0);
.
Binary (_, _, _, Lop, Rop, {Less}, TypeCode) ?
Code (Lop); Code (Lop::Co);
Code (Rop); Code (Rop::Co);
Emit (LES, TypeCode, 0);
.
Unary (Expr := Expr) ?
Code (Expr); Code (Expr::Co);
Emit (INV, 0, 0);
.
IntConst (Value := Value) ?
Emit (LDC, IntType, Value);
.
RealConst (Value := Value) ?
EmitReal (LDC, RealType, Value);
.
BoolConst (Value := {TRUE}) ?
Emit (LDC, BoolType, TrueCode);
.
BoolConst (Value := {FALSE}) ?
Emit (LDC, BoolType, FalseCode);
.
Index (_, _, _, Adr, Expr, Array (Type, Lwb, Upb, _)) ?
Code (Adr); Code (Adr::Co);
Code (Expr); Code (Expr::Co);
Emit (CHK, Lwb, Upb);
Emit (LDC, IntType, Lwb);
Emit (SUB, IntType, 0);
Emit (IXA, TypeSize (Type), 0);
.
Ident (_, _, _, Ident, Definitions.Var (Level := Level, Offset := Offset), level) ?
Emit (LDA, level - Level, Offset);
.
Content (Next) ?
Emit (LDI, 0, 0);
Code (Next);
.
IntToReal (Next) ?
Emit (FLT, 0, 0);
Code (Next);
.
.FT "ICodeInter.md"
DEFINITION MODULE ICodeInter;
CONST (* coding of OpCode parameters *)
IntType = 1;
RealType = 2;
BoolType = 3;
FalseCode = 0;
TrueCode = 1;
(* ICode instructions *)
TYPE OpCode = (LDA, LDC, LDI, STI, JMP, FJP, ADD, SUB, MUL, INV,
LES, IXA, FLT, WRI, REA, MST, JSR, ENT, RET, CHK);
PROCEDURE Emit (oc: OpCode; Param1, Param2: CARDINAL);
PROCEDURE EmitReal (oc: OpCode; Param1: CARDINAL; Param2: REAL);
(* repeated calls of 'Emit' and 'EmitReal' write *)
(* the program into 'Code', starting at Code [0]. *)
PROCEDURE WriteCode; (* 'Code' is written on StdOut *)
PROCEDURE Interpret; (* executes ICode program *)
END ICodeInter.
.FT "ICodeInter.mi"
IMPLEMENTATION MODULE ICodeInter;
FROM StdIO IMPORT ReadI, ReadR, WriteCard, WriteI, WriteR, WriteS, WriteNl;
CONST MaxCode = 30000;
MaxStore = 10000;
SL = 1; (* static link *) (* activation record organization *)
DL = 2; (* dynamic link *)
RA = 3; (* return address *)
TYPE Ptype = CARDINAL; (* type of first parameter *)
Qtype = RECORD
CASE : INTEGER OF
| 1: qc: CARDINAL
| 2: qr: REAL
END
END;
CodeRange = [0..MaxCode]; (* type of second parameter *)
StoreRange = [0..MaxStore];
StoreType = (Undef, Int, Real, Bool, Adr, Mark);
VAR Code : ARRAY CodeRange OF RECORD (* the program *)
OP : OpCode;
P : Ptype;
Q : Qtype;
END;
Store : ARRAY StoreRange OF RECORD (* the data *)
CASE Stype: StoreType OF
Int : Vi : INTEGER
| Real : Vr : REAL
| Bool : Vb : BOOLEAN
| Adr : Va : StoreRange
| Mark : Vm : CodeRange
END;
END;
PC, (* program address register *)
LastPC (* highest used code address *)
: CodeRange;
(* address registers *)
AP, (* points to the beginning of an activation record *)
SP (* points to top of the stack *)
: StoreRange;
OpCodeText : ARRAY OpCode OF ARRAY [0..2] OF CHAR;
PROCEDURE Emit (oc: OpCode; Param1, Param2: CARDINAL);
BEGIN
WITH Code [PC] DO
OP := oc;
P := Param1;
Q.qc := Param2
END;
LastPC := PC;
INC (PC);
END Emit;
PROCEDURE EmitReal (oc: OpCode; Param1: CARDINAL; Param2: REAL);
BEGIN
WITH Code [PC] DO
OP := oc;
P := Param1;
Q.qr := Param2
END;
LastPC := PC;
INC (PC);
END EmitReal;
PROCEDURE WriteInstr (Code: OpCode; Param1: Ptype; Param2: Qtype);
BEGIN
WriteS (OpCodeText [Code]);
CASE Code OF
LDC: (* two parameters *)
WriteI (Param1, 5);
CASE Param1 OF
IntType,
BoolType: WriteI (Param2.qc, 5);
| RealType: WriteR (Param2.qr, 5, 5, 1);
END
| LDA, JSR, CHK: (* two parameters *)
WriteI (Param1, 5);
WriteI (Param2.qc, 5);
| MST, ENT, IXA, LES, JMP,
FJP, ADD, MUL, REA, WRI: (* one parameter *)
WriteI (Param1, 5);
| LDI, STI, RET, FLT, INV, (* no parameter *)
SUB:
END;
WriteNl;
END WriteInstr;
PROCEDURE WriteCode;
VAR pc : CARDINAL;
BEGIN
WriteNl;
WriteS ("Code: (Codelength ="); WriteCard (LastPC+1,4);
WriteS (")");
WriteNl;
IF LastPC # 0 THEN
FOR pc := 0 TO LastPC DO
WriteI (pc, 5);
WriteS (": ");
WITH Code [pc] DO
WriteInstr (OP, P, Q)
END
END
END;
WriteNl;
END WriteCode;
PROCEDURE WriteStore;
VAR Sptr : StoreRange;
BEGIN
WriteNl;
WriteS ("Store: (Index, Elementtype, Contents)"); WriteNl;
FOR Sptr := SP TO 0 BY -1 DO
IF AP = Sptr THEN
WriteS (" AP ->"); WriteI (Sptr, 4);
ELSE
WriteI (Sptr, 12);
END;
WITH Store [Sptr] DO
CASE Stype OF
| Int : WriteS (" Int ");
WriteI (Vi, 8);
| Real : WriteS (" Real");
WriteR (Vr, 8, 8, 1);
| Bool : WriteS (" Bool");
IF Vb THEN
WriteS (" TRUE")
ELSE
WriteS (" FALSE")
END
| Adr : WriteS (" Adr ");
WriteI (Va, 8);
| Mark : WriteS (" Mark");
WriteI (Vm, 8);
ELSE WriteS (" Stype not defined");
END;
WriteNl;
END;
END;
WriteNl
END WriteStore;
PROCEDURE Interpret;
VAR OP : OpCode; (* instruction register *)
P : Ptype;
Q : Qtype;
sr1, sr2 : StoreRange;
PROCEDURE CheckStore (p:CARDINAL);
BEGIN
IF p > MaxStore THEN WriteS ("Store overflow"); END;
END CheckStore;
PROCEDURE Base (Ld : CARDINAL): StoreRange;
(* walks Ld times back the static link chain *)
VAR Sr : StoreRange;
BEGIN
Sr := AP;
WHILE Ld>0 DO
Sr := Store [Sr].Vm;
DEC (Ld);
END;
RETURN Sr;
END Base;
BEGIN
PC := 0;
REPEAT
OP := Code [PC].OP; (* fetch instruction *)
P := Code [PC].P;
Q := Code [PC].Q;
INC(PC);
CASE OP OF (* execute instruction *)
(* load instructions *)
| LDA : INC (SP);
Store [SP].Va := Base(P) + Q.qc;
Store [SP].Stype := Adr;
| LDC : INC (SP);
CASE P OF
IntType : Store [SP].Vi := Q.qc;
Store [SP].Stype := Int;
| RealType : Store [SP].Vr := Q.qr;
Store [SP].Stype := Real;
| BoolType : Store [SP].Vb := Q.qc = TrueCode;
Store [SP].Stype := Bool;
END
| LDI : Store [SP] := Store [Store [SP].Va];
(* store instructions *)
| STI : Store [Store [SP-1].Va] := Store [SP];
SP := SP-2;
(* jump instructions *)
| JMP : PC := P
| FJP : IF NOT Store [SP].Vb THEN
PC := P;
END;
DEC (SP);
(* arithmetic instructions *)
| ADD : DEC (SP);
CASE P OF
IntType : Store[SP].Vi := Store[SP].Vi + Store[SP+1].Vi;
| RealType : Store[SP].Vr := Store[SP].Vr + Store[SP+1].Vr;
END
| SUB : DEC (SP);
Store[SP].Vi := Store[SP].Vi - Store[SP+1].Vi;
| MUL : DEC (SP);
CASE P OF
IntType : Store[SP].Vi := Store[SP].Vi * Store[SP+1].Vi;
| RealType : Store[SP].Vr := Store[SP].Vr * Store[SP+1].Vr;
END
(* logic instructions *)
| INV : Store[SP].Vb := NOT Store[SP].Vb;
| LES : DEC (SP);
CASE P OF
IntType : Store[SP].Vb := Store[SP].Vi < Store[SP+1].Vi;
| RealType : Store[SP].Vb := Store[SP].Vr < Store[SP+1].Vr;
| BoolType : Store[SP].Vb := Store[SP].Vb < Store[SP+1].Vb;
END;
Store [SP].Stype := Bool
(* address calculating instructions *)
| IXA : DEC (SP); (* P=number of storage units *)
Store [SP].Va := P*Store [SP+1].Va + Store [SP].Va;
(* convert instructions *)
| FLT : Store[SP].Vr := FLOAT(CARDINAL(Store[SP].Vi));
Store[SP].Stype := Real;
(* input-output instructions *)
| WRI : CASE P OF
IntType : WriteI (Store[SP].Vi,5); WriteNl;
| RealType : WriteR (Store[SP].Vr,5,5,1); WriteNl;
| BoolType : IF Store [SP].Vb THEN
WriteS (" 1"); WriteNl;
ELSE
WriteS (" 0"); WriteNl;
END
END;
DEC (SP)
| REA : INC (SP);
CASE P OF
IntType : Store[SP].Vi := ReadI();
Store[SP].Stype := Int
| RealType : Store[SP].Vr := ReadR();
Store[SP].Stype := Real
| BoolType : Store[SP].Vb := ReadI() = 1;
Store[SP].Stype := Bool;
END
(* subroutine handling instructions *)
| MST : (* P=(level difference of calling and called procedure)+1 *)
Store [SP+SL].Stype := Adr;
Store [SP+SL].Va := Base (P);
Store [SP+DL].Stype := Adr;
Store [SP+DL].Va := AP;
Store [SP+RA].Stype := Mark;
(* return address is patched in JSR (after *)
SP := SP+3; (* parameter passing) *)
| JSR : AP := SP-(P+2); (* P=number of locations for parameters *)
Store [AP+2].Vm := PC;
PC := Q.qc; (* Q=entry point *)
| ENT : sr2 := SP+P; (* P=length of local data segment *)
FOR sr1 := SP+1 TO sr2 DO
Store [sr1].Stype := Undef;
END;
CheckStore(sr2);
SP := sr2;
| RET : SP := AP-1;
PC := Store [SP+RA].Vm;
AP := Store [SP+DL].Va;
(* check instructions *)
| CHK : IF (Store [SP].Vi < INTEGER(P)) OR
(Store [SP].Vi > INTEGER(Q.qc)) THEN
WriteS ("range check error");
WriteNl;
END
ELSE WriteS ("wrong OpCode :"); WriteS (OpCodeText [OP]); WriteNl;
END;
UNTIL PC = 0;
END Interpret;
BEGIN
OpCodeText [LDA] := "LDA";
OpCodeText [LDC] := "LDC";
OpCodeText [LDI] := "LDI";
OpCodeText [STI] := "STI";
OpCodeText [JMP] := "JMP";
OpCodeText [FJP] := "FJP";
OpCodeText [ADD] := "ADD";
OpCodeText [SUB] := "SUB";
OpCodeText [MUL] := "MUL";
OpCodeText [INV] := "INV";
OpCodeText [LES] := "LES";
OpCodeText [IXA] := "IXA";
OpCodeText [FLT] := "FLT";
OpCodeText [WRI] := "WRI";
OpCodeText [REA] := "REA";
OpCodeText [MST] := "MST";
OpCodeText [JSR] := "JSR";
OpCodeText [ENT] := "ENT";
OpCodeText [RET] := "RET";
OpCodeText [CHK] := "CHK";
Store [0].Stype := Undef;
Store [SL].Stype := Adr; Store [SL].Va := 0;
Store [DL].Stype := Adr; Store [DL].Va := 0;
Store [RA].Stype := Mark; Store [RA].Vm := 0;
PC := 0;
SP := 3;
AP := 1;
END ICodeInter.
.FT "minilax.mi"
MODULE minilax;
FROM IO IMPORT CloseIO;
FROM Parser IMPORT Parse;
FROM Tree IMPORT TreeRoot;
FROM Semantics IMPORT BeginSemantics, Eval;
FROM ICode IMPORT Code;
FROM ICodeInter IMPORT Interpret;
VAR ErrorCount : CARDINAL;
BEGIN
ErrorCount := Parse ();
BeginSemantics;
Eval (TreeRoot);
IF ErrorCount = 0 THEN
Code (TreeRoot);
Interpret;
END;
CloseIO;
END minilax.
.)l
.fi
.sz 12
.[]
.[-
.ds [F Gro87a
.ds [A J\*(p] Grosch
.ds [T Reusable Software - A Collection of Modula-Modules
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 4
.ds [N 4
.ds [D Sep. 1987
.][
.[-
.ds [F Gro87b
.ds [A J\*(p] Grosch
.ds [T Rex - A Scanner Generator
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 5
.ds [N 5
.ds [D Dec. 1987
.][
.[-
.ds [F GrV88
.ds [A J\*(p] Grosch
.as [A \*(n]B\*(p] Vielsack
.ds [T The Parser Generators Lalr and Ell
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 8
.ds [N 8
.ds [D Apr. 1988
.][
.[-
.ds [F Gro89
.ds [A J\*(p] Grosch
.ds [T Ag - An Attribute Evaluator Generator
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 16
.ds [N 16
.ds [D Aug. 1989
.][
.[-
.ds [F GrE90
.ds [A J\*(p] Grosch
.as [A \*(n]H\*(p] Emmelmann
.ds [T A Tool Box for Compiler Construction
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 20
.ds [N 20
.ds [D Jan. 1990
.][
.[-
.ds [F Gro91a
.ds [A J\*(p] Grosch
.ds [T Ast - A Generator for Abstract Syntax Trees
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 15
.ds [N 15
.ds [D Sep. 1991
.][
.[-
.ds [F Gro91b
.ds [A J\*(p] Grosch
.ds [T Puma - A Generator for the Transformation of Attributed Trees
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 26
.ds [N 26
.ds [D July 1991
.][
.[-
.ds [F Gro91c
.ds [A J\*(p] Grosch
.ds [T Preprocessors
.ds [I GMD Forschungsstelle an der Universit\\*:at Karlsruhe
.ds [R Compiler Generation Report No. 24
.ds [N 24
.ds [D Feb. 1991
.][
.[-
.ds [F NAJ76
.ds [A K\*(p]\*(a]V\*(p] Nori
.as [A \*(c]U\*(p] Ammann
.as [A \*(c]K\*(p] Jensen
.as [A \*(c]H\*(p]\*(a]H\*(p] N\\*:ageli
.as [A \*(m]C\*(p] Jacobi
.ds [T The Pascal-P Compiler: Implementation Notes
.nr [P 0
.ds [P 53
.ds [R Bericht 10
.ds [I Eidgen\\*:ossische Technische Hochschule
.ds [C Z\\*:urich
.ds [D July 1976
.][
.[-
.ds [F WaG84
.ds [A W\*(p]\*(a]M\*(p] Waite
.as [A \*(n]G\*(p] Goos
.ds [T Compiler Construction
.ds [I Springer Verlag
.ds [C New York, NY
.ds [D 1984
.][
.bp 1
.lp
.b Contents
.sp
.xp